Apa cara yang tepat untuk melakukan metode pencarian RESTful yang kompleks?

44

Mengikuti prinsip REST, saya ingin membuat metode GET untuk API saya yang melakukan pencarian menggunakan beberapa kriteria dan mengembalikan hasilnya ke klien. Masalahnya adalah bahwa kriteria dapat memiliki hingga 14 parameter, salah satunya adalah daftar objek yang kompleks, jadi ...

  • Saya bahkan tidak tahu apakah mungkin untuk menyandikan / mendekodekan objek kompleks ini ke / dari parameter url.

  • Saya tidak menghitung berapa lama url bisa didapat tetapi saya yakin itu akan cukup besar dan mungkin mencapai batas panjang url?

Juga, pencarian harus menunjukkan hasil dalam "waktu nyata", maksud saya, setiap kali pengguna mengubah sesuatu dari formulir pencarian dia harus dapat melihat hasil baru tanpa menekan tombol "pencarian".

Bisakah Anda mengklarifikasi poin-poin ini kepada saya dan apa saran Anda untuk membuat metode pencarian yang tenang dengan banyak parameter?

anat0lius
sumber
3
Sebagai tambahan (saya perhatikan bahwa tidak satu pun dari dua jawaban pada saat penulisan menyebutkan bagian waktu nyata), pencarian waktu nyata biasanya hanya mengirim permintaan yang diperbarui berulang-ulang. Misalnya, sementara Anda sedang mengetik, Anda akan mengirim permintaan untuk hal-hal seperti search?q=t, search?q=te, search?q=test, dan sebagainya. Pertimbangkan untuk membatasi seberapa sering permintaan dikirimkan untuk menghindari melukai server Anda. Anda juga bisa mengembalikan banyak informasi dan di sisi klien melakukan penyaringan. Itu bekerja dengan baik jika pengguna memasukkan kategori luas yang dapat mempersempit banyak hal.
Kat
2
Apa pun solusinya, pastikan untuk bertukar data dalam format netral teknologi. Jadi jangan gunakan representasi internal atau akan lebih sulit untuk menulis klien untuk API Anda.
Kwebble
Bergantung pada kebutuhan Anda, perpustakaan ini dapat melakukan pekerjaan: github.com/jirutka/rsql-parser . Ini memiliki ekstensi JPA jika Anda menggunakan JPA, atau Anda dapat menulis Pengunjung Anda sendiri untuk dipetakan ke API mesin pencari Anda. Dan Anda dapat menambahkan operator Anda sendiri.
Walfrat

Jawaban:

54

Sebelum Anda membaca jawaban saya, saya ingin mengatakan bahwa saya setuju dengan @Neil. Kita harus memilih pertempuran kita. Kami biasanya ingin melakukan yang terbaik, tetapi kadang-kadang ada terlalu sedikit ruang untuk diskusi dan kami harus membuat keputusan yang bertentangan dengan keinginan kami.

Bagaimanapun, dalam jawaban Neil, aku kehilangan satu hal lagi. Dokumentasi . Hanya untuk memastikan bahwa pengembang tahu bahwa permintaan POST /searchaman.

Itu kata.

1. Berikan kesempatan untuk MENDAPATKAN

Pertimbangkan GETopsi tersebut terlebih dahulu. Lihat panjang maksimal URL pertanyaan ini . Evaluasi apakah string kueri terpanjang Anda lebih dari 2000 karakter. Jika tidak, dan Anda tidak mengharapkannya, ikuti saja GET. Ini mungkin tampak jelek, tetapi setidaknya Anda dapat mem-bookmark URL dan, tentu saja, ia memiliki semua keuntungan yang didapat dari semantik metode (idempotensi, aman, dan cache)

1.1 Cobalah meng-encode string kueri

Misalnya, dalam basis 64. Bahkan javascript mendukung basis 64 encodings .

Beginilah cara kerjanya:

  1. Membangun JSON dengan semua filter dan menormalkannya.
  2. Parsing ke string
  3. Enkode itu
  4. Kirim JSON yang disandikan sebagai param ( /search?q=SGVsbG8gV29ybGQh....) permintaan .
  5. Di sisi server, decode param q .
  6. Deserialize string JSON

Sebelumnya, buat string JSON terpanjang yang mungkin, disandikan dan ambil panjangnya. Evaluasi apakah string yang disandikan cocok dengan URL. Saya telah menerapkan cuplikan berikut pada Fiddle.js untuk Anda uji. (Saya harap ini masih berfungsi) 1

Encode Base 64 bersifat deterministik dan dapat dibalik, sehingga tidak ada kemungkinan tabrakan.

Dengan kueri yang disandikan, kami juga dapat menyimpan pencarian di DB, mem-bookmark URL, membagikan tautan, dll. Dan, tentu saja, kami tidak harus melarikan diri / menghilangkan jejak string.

1.2 Coba dengan alias

Membaca blog ini tentang cara mendesain API REST, saya ingat satu alternatif lagi. Alias ​​untuk pertanyaan umum .

Saya menemukan ini menarik karena alasan berikutnya

  • Persingkat panjang string kueri. Itu membuat API lebih bersih dan ramah pengguna

    DAPATKAN / tiket /? Status = ditutup & ditutupAt = xxx vs GET / tiket / baru-baru ini ditutup /

  • Dapat dikombinasikan dengan alias atau parameter permintaan lainnya.

    DAPATKAN / tiket /? Status = ditutup & ditutupAt = xxx & dalam = 30 mnt vs GET / tiket / baru-baru ini ditutup /? Dalam = 30 mnt

  • Kami dapat menggabungkan alias dengan string kueri yang disandikan

    DAPATKAN / tiket /? Status = ditutup & ditutupAt = xxx & dalam = 30 mnt vs GET / tiket / baru-baru ini ditutup /? Q = SGVsbG8g ...


1: Saya telah menggunakan JSON, tetapi kita bisa menggunakan format lain segera setelah kita dapat deserialize di sisi server.

Laiv
sumber
2
Ini praktis dan benar. Perlu juga dicatat bahwa sebagian besar bahasa pemrograman membuatnya sepele untuk mengubah hash menjadi string kueri sehingga memulai dengan tindakan GET sangat mudah dilakukan.
Aluan Haddad
1
Saya suka Spring stackoverflow.com/questions/16942193/... Saya tidak percaya itu berhasil pada percobaan pertama: D. Tentang panjang url, kurang dari 1k, meskipun kita masih perlu mengulangi spesifikasi.
anat0lius
Lalu, pergi dengan GET. Untuk kesederhanaan. Dengan Spring MVC, Anda dapat mencapai pemetaan yang sama dengan GET. Carilah Spring's WebArgumentResolver ;-)
Laiv
Base64 mengembang ukuran payload sekitar 4/3. Sementara urlencoding dapat membuatnya 3/1 untuk karakter khusus, permintaan dengan sebagian besar karakter yang aman akan mempertahankan ukuran yang sama. Apakah ada alasan lain untuk menggunakan base64?
villasv
Tidak juga. Saya hanya tidak suka (tidak) URL melarikan diri. Overweigth dari payload adalah pengorbanan di sini. Itu masih harus sesuai dengan ukuran maksimal GET per permintaan. Itu sebabnya saya membuat cuplikannya. Bagi pengguna untuk mencoba. Ketika saya menulis jawabannya, saya mengutamakan semantik web atas detail implementasi. Inti dari jawabannya adalah "terus mencoba dengan MENDAPATKAN". Temukan jalan Anda atau gunakan semua yang saya bagikan ini dengan Anda.
Laiv
13

Jika yang Anda miliki adalah palu, semuanya terlihat seperti paku. Tampaknya masalah di sini adalah Anda mencoba mengubah halaman pencarian menjadi RESTful, dan ini sepertinya bukan pola yang umum untuk dipecahkan oleh desain RESTful.

Cukup pergi dengan permintaan POST dengan parameter yang disediakan oleh pengguna untuk mendapatkan informasi yang Anda butuhkan dari backend. Saya berasumsi Anda tidak perlu melakukan apa pun selain melakukan pencarian, jadi Anda tidak perlu memasukkan halaman ini. Cukup tambahkan a / search ke akhir URL Anda sehingga Anda tidak berisiko mengalami konflik dengan halaman / pengguna Anda yang akan TENANG.

Neil
sumber
2
@LiLou_: Untuk persyaratan itu, hanya ada dua kemungkinan realistis: 1. Baca semua data ke front-end Anda dan lakukan pemfilteran di sana. Ini mungkin menjadi penghalang dalam jumlah memori yang diperlukan. 2. Lakukan permintaan baru ke server untuk setiap perubahan dalam kriteria pencarian. Tidak masalah apakah ini POST atau permintaan GET, tetapi latensi jaringan yang terlibat mungkin mengganggu perasaan pengguna tentang pembaruan "waktu nyata".
Bart van Ingen Schenau
2
Saya setuju untuk tidak setuju, POST berarti sesuatu yang lain secara semantik. Saya akan menyarankan untuk pergi dengan GET dan melewati semua data filter dalam parameter kueri sekarang jika ada terlalu banyak parameter maka itu adalah kesalahan pada level aplikasi.
CodeYogi
2
@CodeYogi terkadang pelanggan tidak memberi Anda ruang untuk berdiskusi. Saya telah menerapkan halaman tampilan seperti Excel dengan masing-masing 40-50 kolom dengan filternya sendiri. Dan tentu saja bisa diurutkan.
Ngomong
1
@Laiv dalam hal ini perlu ada diskusi serius karena POST dimaksudkan untuk perubahan dalam kondisi server. Kasus penggunaan seperti ini tidak luar biasa karena itu harus dijaga tanpa peretasan.
CodeYogi
2
Dalam kasus ini, dokumentasi adalah suatu keharusan. Saya telah melakukan diskusi serius dengan pelanggan mengenai kegunaan aplikasi mereka. Kemudian saya terbukti benar karena pengguna akhir setuju dengan saya. Namun, terkadang Anda harus memilih pertempuran Anda.
Laiv
0

Ini sepenuhnya tergantung pada apa model API Anda: Karena tidak ada atau sebagai kata kerja.

Jika API bukan, maka Anda mungkin ingin mendapatkan daftar objek sebagai berikut:

GET: /api/v1/objects

Dalam hal ini Anda harus mengirim data sebagai parameter permintaan. Jadi, Anda harus menggambarkan parameter Anda sebagai daftar kunci dari nilai kunci:

GET: /api/v1/objects

key1 : val1
key2.key1 : val 21
key2.key2 : val 22
....

Beberapa platform mendukung resolver parameter khusus (mis. Spring MVC), dan Anda dapat mengubah params menjadi objek.

Mostafa
sumber