Saya membutuhkan bantuan menangani case edge yang aneh dengan API paginasi yang saya bangun.
Seperti banyak API, ini memberikan hasil yang besar. Jika Anda query / foos, Anda akan mendapatkan 100 hasil (mis. Foo # 1-100), dan tautan ke / foos? Page = 2 yang akan mengembalikan foo # 101-200.
Sayangnya, jika foo # 10 dihapus dari kumpulan data sebelum konsumen API membuat kueri berikutnya, / foos? Page = 2 akan diimbangi dengan 100 dan mengembalikan foo # 102-201.
Ini adalah masalah bagi konsumen API yang mencoba menarik semua foo - mereka tidak akan menerima foo # 101.
Apa praktik terbaik untuk menangani ini? Kami ingin menjadikannya seringan mungkin (yaitu menghindari sesi penanganan untuk permintaan API). Contoh dari API lain akan sangat dihargai!
sumber
Jawaban:
Saya tidak sepenuhnya yakin bagaimana data Anda ditangani, jadi ini mungkin atau mungkin tidak berfungsi, tetapi sudahkah Anda mempertimbangkan membuat halaman dengan bidang stempel waktu?
Ketika Anda query / foos Anda mendapatkan 100 hasil. API Anda kemudian harus mengembalikan sesuatu seperti ini (dengan asumsi JSON, tetapi jika perlu XML prinsip-prinsip yang sama dapat diikuti):
Hanya sebuah catatan, hanya menggunakan satu cap waktu bergantung pada 'batas' implisit dalam hasil Anda. Anda mungkin ingin menambahkan batas eksplisit atau juga menggunakan
until
properti.Stempel waktu dapat ditentukan secara dinamis menggunakan item data terakhir dalam daftar. Ini sepertinya kurang lebih bagaimana paginasi Facebook dalam API Grafiknya (gulir ke bawah untuk melihat tautan pagination dalam format yang saya berikan di atas).
Satu masalah mungkin jika Anda menambahkan item data, tetapi berdasarkan deskripsi Anda, sepertinya akan ditambahkan ke bagian akhir (jika tidak, beri tahu saya dan saya akan melihat apakah saya dapat memperbaiki ini).
sumber
Anda memiliki beberapa masalah.
Pertama, Anda memiliki contoh yang Anda kutip.
Anda juga memiliki masalah serupa jika baris dimasukkan, tetapi dalam hal ini pengguna mendapatkan data duplikat (bisa dibilang lebih mudah dikelola daripada data yang hilang, tetapi masih menjadi masalah).
Jika Anda tidak snapshotting set data asli, maka ini hanya fakta kehidupan.
Anda dapat meminta pengguna membuat snapshot eksplisit:
Yang menghasilkan:
Maka Anda dapat halaman itu sepanjang hari, karena sekarang statis. Ini bisa sangat ringan, karena Anda bisa menangkap kunci dokumen yang sebenarnya daripada seluruh baris.
Jika use case hanya karena pengguna Anda ingin (dan membutuhkan) semua data, maka Anda bisa memberikannya kepada mereka:
dan cukup kirim seluruh kit.
sumber
Jika Anda memiliki pagination, Anda juga mengurutkan data dengan beberapa kunci. Mengapa tidak membiarkan klien API memasukkan kunci elemen terakhir dari koleksi yang sebelumnya dikembalikan dalam URL dan menambahkan
WHERE
klausa ke permintaan SQL Anda (atau sesuatu yang setara, jika Anda tidak menggunakan SQL) sehingga hanya mengembalikan elemen-elemen yang kuncinya lebih besar dari nilai ini?sumber
Mungkin ada dua pendekatan tergantung pada logika sisi server Anda.
Pendekatan 1: Ketika server tidak cukup pintar untuk menangani status objek.
Anda dapat mengirim semua id unik yang di-cache ke server, misalnya ["id1", "id2", "id3", "id4", "id5", "id6", "id7", "id8", "id9", "id10"] dan parameter boolean untuk mengetahui apakah Anda meminta catatan baru (tarik untuk menyegarkan) atau catatan lama (muat lebih banyak).
Sever Anda harus bertanggung jawab untuk mengembalikan catatan baru (memuat lebih banyak catatan atau catatan baru melalui tarikan untuk menyegarkan) serta id dari catatan yang dihapus dari ["id1", "id2", "id3", "id4", "id5", " id6 "," id7 "," id8 "," id9 "," id10 "].
Contoh: - Jika Anda meminta memuat lebih dari itu permintaan Anda akan terlihat seperti ini: -
Sekarang anggaplah Anda meminta catatan lama (muat lebih banyak) dan anggap catatan "id2" diperbarui oleh seseorang dan catatan "id5" dan "id8" dihapus dari server maka respons server Anda akan terlihat seperti ini: -
Tetapi dalam hal ini jika Anda memiliki banyak catatan dalam cache lokal misalkan 500, maka string permintaan Anda akan terlalu panjang seperti ini: -
Pendekatan 2: Ketika server cukup pintar untuk menangani status objek sesuai tanggal.
Anda dapat mengirim id catatan pertama dan catatan terakhir dan waktu permintaan sebelumnya. Dengan cara ini permintaan Anda selalu kecil bahkan jika Anda memiliki banyak catatan dalam cache
Contoh: - Jika Anda meminta memuat lebih dari itu permintaan Anda akan terlihat seperti ini: -
Server Anda bertanggung jawab untuk mengembalikan id dari catatan yang dihapus yang dihapus setelah last_request_time serta mengembalikan catatan yang diperbarui setelah last_request_time antara "id1" dan "id10".
Tarik Untuk Menyegarkan: -
Muat lebih banyak
sumber
Mungkin sulit untuk menemukan praktik terbaik karena sebagian besar sistem dengan API tidak mengakomodasi skenario ini, karena ini merupakan keunggulan ekstrim, atau mereka biasanya tidak menghapus catatan (Facebook, Twitter). Facebook sebenarnya mengatakan setiap "halaman" mungkin tidak memiliki jumlah hasil yang diminta karena penyaringan dilakukan setelah pagination. https://developers.facebook.com/blog/post/478/
Jika Anda benar-benar perlu mengakomodasi kasing tepi ini, Anda perlu "mengingat" di mana Anda tinggalkan. Saran jandjorgensen hampir tepat, tapi saya akan menggunakan bidang yang dijamin unik seperti kunci primer. Anda mungkin perlu menggunakan lebih dari satu bidang.
Mengikuti aliran Facebook, Anda dapat (dan harus) melakukan cache halaman yang sudah diminta dan hanya mengembalikannya dengan baris yang dihapus difilter jika mereka meminta halaman yang sudah mereka minta.
sumber
Pagination pada umumnya adalah operasi "pengguna" dan untuk mencegah kelebihan pada komputer dan otak manusia, Anda biasanya memberikan subset. Namun, daripada berpikir bahwa kita tidak mendapatkan seluruh daftar, mungkin lebih baik untuk bertanya apakah itu penting?
Jika diperlukan tampilan pengguliran langsung yang akurat, API REST yang bersifat permintaan / respons tidak cocok untuk tujuan ini. Untuk ini, Anda harus mempertimbangkan WebSockets atau HTML5 Server-Sent Events untuk memberi tahu ujung depan Anda ketika berhadapan dengan perubahan.
Sekarang jika ada kebutuhan untuk mendapatkan snapshot data, saya hanya akan memberikan panggilan API yang menyediakan semua data dalam satu permintaan tanpa pagination. Pikiran Anda, Anda akan memerlukan sesuatu yang akan melakukan streaming output tanpa memuat sementara di memori jika Anda memiliki kumpulan data yang besar.
Untuk kasus saya, saya secara implisit menunjuk beberapa panggilan API untuk memungkinkan mendapatkan seluruh informasi (terutama data tabel referensi). Anda juga dapat mengamankan API ini sehingga tidak akan merusak sistem Anda.
sumber
Opsi A: Paget Keyset dengan Stempel Waktu
Untuk menghindari kekurangan dari pagination offset yang telah Anda sebutkan, Anda dapat menggunakan pagination berbasis keyset. Biasanya, entitas memiliki stempel waktu yang menyatakan waktu kreasi atau modifikasi mereka. Stempel waktu ini dapat digunakan untuk paginasi: Cukup berikan stempel waktu elemen terakhir sebagai parameter kueri untuk permintaan berikutnya. Server, pada gilirannya, menggunakan timestamp sebagai kriteria filter (misalnya
WHERE modificationDate >= receivedTimestampParameter
)Dengan cara ini, Anda tidak akan kehilangan elemen apa pun. Pendekatan ini harus cukup baik untuk banyak kasus penggunaan. Namun, ingatlah hal-hal berikut:
Anda dapat memperkecil kekurangan tersebut dengan meningkatkan ukuran halaman dan menggunakan cap waktu dengan presisi milidetik.
Opsi B: Perpanjangan Keyset Diperpanjang dengan Token Berlanjut
Untuk menangani kekurangan yang disebutkan pada pagination keyset normal, Anda dapat menambahkan offset ke timestamp dan menggunakan apa yang disebut "Token Berlanjut" atau "Kursor". Offset adalah posisi elemen relatif terhadap elemen pertama dengan stempel waktu yang sama. Biasanya, token memiliki format seperti
Timestamp_Offset
. Itu diteruskan ke klien dalam respons dan dapat dikirim kembali ke server untuk mengambil halaman berikutnya.Token "1512757072_2" menunjuk ke elemen terakhir halaman dan menyatakan "klien sudah mendapatkan elemen kedua dengan stempel waktu 1512757072". Dengan cara ini, server tahu ke mana harus melanjutkan.
Harap diingat bahwa Anda harus menangani kasus di mana elemen diubah di antara dua permintaan. Ini biasanya dilakukan dengan menambahkan checksum ke token. Checksum ini dihitung atas ID semua elemen dengan cap waktu ini. Jadi kita berakhir dengan format token seperti ini:
Timestamp_Offset_Checksum
.Untuk informasi lebih lanjut tentang pendekatan ini lihat posting blog " Paginasi API Web dengan Token Lanjutan ". Kelemahan dari pendekatan ini adalah implementasi yang rumit karena ada banyak kasus sudut yang harus diperhitungkan. Itu sebabnya perpustakaan seperti kelanjutan-token bisa berguna (jika Anda menggunakan bahasa Java / a JVM). Penafian: Saya penulis posting dan penulis pendamping perpustakaan.
sumber
Saya pikir saat ini api Anda benar-benar merespons seperti seharusnya. 100 catatan pertama pada halaman dalam urutan keseluruhan objek yang Anda pertahankan. Penjelasan Anda memberi tahu bahwa Anda menggunakan semacam nomor pesanan untuk menentukan urutan objek Anda untuk pagination.
Sekarang, jika Anda ingin halaman 2 harus selalu mulai dari 101 dan berakhir pada 200, maka Anda harus membuat jumlah entri pada halaman sebagai variabel, karena mereka dapat dihapus.
Anda harus melakukan sesuatu seperti kodesemu di bawah ini:
sumber
Untuk menambah jawaban ini oleh Kamilk: https://www.stackoverflow.com/a/13905589
sumber
Saya sudah berpikir panjang dan keras tentang ini dan akhirnya berakhir dengan solusi yang akan saya jelaskan di bawah ini. Ini adalah langkah yang cukup besar dalam kompleksitas tetapi jika Anda melakukan langkah ini, Anda akan berakhir dengan apa yang benar-benar Anda cari, yang merupakan hasil deterministik untuk permintaan di masa mendatang.
Contoh Anda dari item yang dihapus hanya ujung gunung es. Bagaimana jika Anda memfilter dengan
color=blue
tetapi seseorang mengubah warna item di antara permintaan? Mengambil semua item dengan cara yang dapat dipercaya secara andal adalah mustahil ... kecuali ... kami menerapkan riwayat revisi .Saya sudah menerapkannya dan itu sebenarnya kurang sulit dari yang saya harapkan. Inilah yang saya lakukan:
changelogs
dengan kolom ID penambahan otomatisid
bidang, tetapi ini bukan kunci utamachangeId
bidang yang merupakan kunci utama dan juga kunci asing untuk changelogs.changelogs
, meraih id dan wakilnya itu ke baru versi entitas, yang kemudian menyisipkan dalam DBchangeId
mewakili snapshot unik dari data yang mendasarinya saat perubahan itu dibuat.changeId
di dalamnya selamanya. Hasilnya tidak akan pernah kedaluwarsa karena tidak akan pernah berubah.sumber
Opsi lain untuk Pagination di RESTFul APIs, adalah menggunakan taut Link yang diperkenalkan di sini . Misalnya Github menggunakannya sebagai berikut:
Nilai yang mungkin untuk
rel
adalah: pertama, terakhir, berikutnya, sebelumnya . Namun dengan menggunakanLink
tajuk, Anda tidak dapat menentukan total_count (jumlah elemen).sumber