REST API Praktik terbaik: Cara menerima daftar nilai parameter sebagai input [ditutup]

410

Kami meluncurkan API REST baru dan saya ingin beberapa masukan komunitas tentang praktik terbaik seputar bagaimana kami seharusnya memiliki parameter input yang diformat:

Saat ini, API kami sangat JSON-sentris (hanya mengembalikan JSON). Perdebatan tentang apakah kita ingin / perlu mengembalikan XML adalah masalah yang terpisah.

Karena output API kami adalah JSON centric, kami telah menempuh jalan di mana input kami sedikit JSON centric dan saya telah berpikir bahwa mungkin nyaman untuk beberapa tetapi aneh pada umumnya.

Misalnya, untuk mendapatkan beberapa detail produk di mana beberapa produk dapat ditarik sekaligus saat ini kami miliki:

http://our.api.com/Product?id=["101404","7267261"]

Haruskah kita menyederhanakan ini sebagai:

http://our.api.com/Product?id=101404,7267261

Atau apakah memiliki input JSON berguna? Lebih sakit?

Kami mungkin ingin menerima kedua gaya tetapi apakah fleksibilitas itu sebenarnya menyebabkan lebih banyak kebingungan dan sakit kepala (rawatan, dokumentasi, dll.)?

Kasus yang lebih kompleks adalah ketika kami ingin menawarkan input yang lebih kompleks. Misalnya, jika kami ingin mengizinkan beberapa filter pada pencarian:

http://our.api.com/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}

Kami tidak selalu ingin menempatkan jenis filter (mis. Jenis produk dan warna) sebagai nama permintaan seperti ini:

http://our.api.com/Search?term=pumas&productType=["Clothing","Bags"]&color=["Black","Red"]

Karena kami ingin mengelompokkan semua input filter bersama.

Pada akhirnya, apakah ini benar-benar penting? Mungkin ada begitu banyak utiliti JSON di luar sana sehingga tipe input tidak terlalu penting.

Saya tahu klien JavaScript kami yang melakukan panggilan AJAX ke API dapat menghargai input JSON untuk membuat hidup mereka lebih mudah.

entah kenapa
sumber

Jawaban:

341

A Step Back

Pertama dan terpenting, REST menggambarkan URI sebagai ID unik yang universal. Terlalu banyak orang yang terjebak dalam struktur URI dan URI mana yang lebih "tenang" daripada yang lain. Argumen ini sama menggelikannya dengan mengatakan menamai seseorang "Bob" lebih baik daripada menamainya "Joe" - kedua nama tersebut mendapatkan pekerjaan "mengidentifikasi seseorang". URI tidak lebih dari nama universal yang unik .

Jadi di mata REST berdebat tentang apakah ?id=["101404","7267261"]lebih tenang daripada ?id=101404,7267261atau \Product\101404,7267261agak sia-sia.

Sekarang, setelah mengatakan itu, berkali-kali bagaimana URI dibangun biasanya dapat berfungsi sebagai indikator yang baik untuk masalah lain dalam layanan tenang. Ada beberapa tanda merah di URI Anda dan pertanyaan secara umum.

Saran

  1. Beberapa URI untuk sumber daya yang sama dan Content-Location

    Kami mungkin ingin menerima kedua gaya tetapi apakah fleksibilitas itu sebenarnya menyebabkan lebih banyak kebingungan dan sakit kepala (rawatan, dokumentasi, dll.)?

    URI mengidentifikasi sumber daya. Setiap sumber daya harus memiliki satu URI kanonik. Ini tidak berarti bahwa Anda tidak dapat memiliki dua URI menunjuk ke sumber daya yang sama tetapi ada cara yang jelas untuk melakukannya. Jika Anda memutuskan untuk menggunakan JSON dan format berbasis daftar (atau format lainnya), Anda perlu memutuskan format mana yang merupakan URI kanonis utama . Semua respons terhadap URI lain yang menunjuk ke "sumber daya" yang sama harus mencakup Content-Locationheader .

    Menempel dengan analogi nama, memiliki beberapa URI seperti memiliki nama panggilan untuk orang-orang. Ini sangat bisa diterima dan sering kali sangat berguna, namun jika saya menggunakan nama panggilan saya mungkin masih ingin tahu nama lengkap mereka - cara "resmi" untuk merujuk orang itu. Dengan cara ini ketika seseorang menyebut seseorang dengan nama lengkapnya, "Nichloas Telsa", saya tahu mereka berbicara tentang orang yang sama yang saya sebut sebagai "Nick".

  2. "Cari" di URI Anda

    Kasus yang lebih kompleks adalah ketika kami ingin menawarkan input yang lebih kompleks. Misalnya, jika kami ingin mengizinkan beberapa filter pada pencarian ...

    Aturan umum saya adalah, jika URI Anda mengandung kata kerja, itu mungkin merupakan indikasi bahwa ada sesuatu yang tidak aktif. URI mengidentifikasi sumber daya, namun mereka tidak boleh menunjukkan apa yang kami lakukan terhadap sumber daya itu. Itu pekerjaan HTTP atau dalam istilah yang tenang, "antarmuka seragam" kami.

    Untuk mengalahkan analogi nama mati, menggunakan kata kerja dalam URI seperti mengubah nama seseorang ketika Anda ingin berinteraksi dengan mereka. Jika saya berinteraksi dengan Bob, nama Bob tidak menjadi "BobHi" ketika saya ingin mengatakan Hai padanya. Demikian pula, ketika kita ingin "mencari" Produk, struktur URI kita seharusnya tidak berubah dari "/ Produk / ..." menjadi "/ Pencarian / ...".

Menjawab Pertanyaan Awal Anda

  1. Mengenai ["101404","7267261"]vs 101404,7267261: Saran saya di sini adalah untuk menghindari sintaks JSON demi kesederhanaan (yaitu tidak mengharuskan pengguna Anda melakukan pengkodean URL ketika Anda tidak benar-benar harus). Ini akan membuat API Anda sedikit lebih bermanfaat. Lebih baik lagi, seperti yang direkomendasikan orang lain, gunakan application/x-www-form-urlencodedformat standar karena mungkin paling akrab bagi pengguna akhir Anda (mis ?id[]=101404&id[]=7267261.). Itu mungkin tidak "cantik", tetapi Pretty URI tidak perlu berarti URI yang Dapat Digunakan. Namun, untuk menegaskan kembali poin awal saya, pada akhirnya ketika berbicara tentang REST, tidak masalah. Jangan terlalu memikirkannya.

  2. Contoh URI pencarian kompleks Anda dapat diselesaikan dengan cara yang hampir sama dengan contoh produk Anda. Saya akan merekomendasikan application/x-www-form-urlencodedformat lagi karena sudah standar yang banyak akrab. Juga, saya akan merekomendasikan menggabungkan keduanya.

URI Anda ...

/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}    

URI Anda setelah dikodekan dengan URI ...

/Search?term=pumas&filters=%7B%22productType%22%3A%5B%22Clothing%22%2C%22Bags%22%5D%2C%22color%22%3A%5B%22Black%22%2C%22Red%22%5D%7D

Dapat diubah menjadi ...

/Product?term=pumas&productType[]=Clothing&productType[]=Bags&color[]=Black&color[]=Red

Selain menghindari persyaratan pengkodean URL dan membuat hal-hal terlihat sedikit lebih standar, sekarang sedikit menyeragamkan API. Pengguna tahu bahwa jika mereka ingin mengambil Produk atau Daftar Produk (keduanya dianggap sebagai "sumber daya" tunggal dalam istilah RESTful), mereka tertarik pada /Product/...URI.

baik
sumber
67
Ingin menindaklanjuti dan perhatikan bahwa []sintaksis tidak selalu didukung (dan meskipun umum, bahkan mungkin melanggar spesifikasi URI). Beberapa server HTTP dan bahasa pemrograman akan lebih suka hanya mengulangi namanya (misalnya productType=value1&productType=value2).
nategood
1
Pertanyaan awal dengan pertanyaan ini .. "/ Cari? Istilah = puma & filter = {" productType ": [" Pakaian "," Tas "]," warna ": [" Hitam "," Merah "]}" diterjemahkan menjadi .. . (productType == pakaian || productType == tas) && (warna == hitam || warna == merah) TETAPI SOLUSI ANDA: / Produk? istilah = pumas & productType [] = Pakaian & productType [] = Pakaian & productType [] = Tas & warna [] = Hitam & warna []] = Merah tampaknya menerjemahkan ke ... Entah (productType == pakaian || productType == tas || warna == hitam || warna == merah) atau Salah satu (productType == pakaian && productType == kantong && warna == black && color == red) Yang sepertinya sedikit berbeda dengan saya. Atau apakah saya salah paham?
Thomas Cheng
2
Bagaimana dengan input dalam permintaan posting? Saya ingin tahu jika kami memperbarui sumber daya, maka apakah itu praktik yang buruk untuk mengirim kueri / filter dan data dalam tubuh dalam format standar. untuk mis. jika saya ingin mengubah data yang terkait dengan pengguna menggunakan api /user/dan di dalam tubuh, saya akan mengirim { q:{}, d: {} }dengan qsebagai permintaan dengan pengguna akan ditanyai dalam DB dan dsebagai data yang dimodifikasi.
molekul
1
Apa yang Anda lakukan ketika daftar itu mungkin sangat besar? Panjang URI terbatas tergantung pada browser. Saya biasanya telah beralih ke permintaan pos dan mengirim daftar di badan. Ada saran di sana?
Troy Cosentino
4
Itu harus SANGAT besar (lihat stackoverflow.com/questions/417142/... ), tapi ya, dalam kasus yang paling ekstrem, Anda mungkin perlu menggunakan isi permintaan. Mengepos pertanyaan untuk pengambilan data adalah salah satu hal yang suka diperdebatkan oleh RESTafarian.
nategood
234

Cara standar untuk meneruskan daftar nilai sebagai parameter URL adalah mengulanginya:

http://our.api.com/Product?id=101404&id=7267261

Sebagian besar kode server akan menafsirkan ini sebagai daftar nilai, meskipun banyak yang memiliki penyederhanaan nilai tunggal sehingga Anda mungkin harus mencari.

Nilai yang dibatasi juga oke.

Jika Anda perlu mengirim JSON ke server, saya tidak suka melihatnya di URL (yang merupakan format berbeda). Secara khusus, URL memiliki batasan ukuran (dalam praktiknya jika tidak secara teori).

Cara saya melihat beberapa melakukan kueri yang rumit RESTfully adalah dalam dua langkah:

  1. POST persyaratan kueri Anda, menerima kembali ID (dasarnya membuat sumber daya kriteria pencarian)
  2. GET pencarian, referensi ID di atas
  3. secara opsional HAPUS persyaratan permintaan jika perlu, tetapi perhatikan bahwa persyaratan itu tersedia untuk digunakan kembali.
Kathy Van Stone
sumber
8
Terima kasih Kathy. Saya pikir saya bersama Anda dan tidak terlalu suka melihat JSON di URL juga. Namun, saya bukan penggemar melakukan posting untuk pencarian yang merupakan operasi GET yang melekat. Bisakah Anda menunjukkan contohnya?
entah apa,
1
Jika kueri dapat berfungsi sebagai parameter sederhana, lakukan itu. Sumbernya berasal dari daftar alamat lain-mendiskusikan: tech.groups.yahoo.com/group/rest-discuss/message/11578
Kathy Van Stone
2
Jika Anda hanya ingin menunjukkan dua sumber, jawaban James Westgate lebih khas
Kathy Van Stone
Ini jawaban yang benar. Dalam waktu dekat saya yakin kita akan melihat beberapa filter = id di (a, b, c, ...) didukung oleh OData atau sesuatu seperti itu.
Bart Calixto
Ini adalah cara kerja HTTP Akka
Joan
20

Pertama:

Saya pikir Anda bisa melakukannya dengan 2 cara

http://our.api.com/Product/<id> : jika Anda hanya ingin satu catatan

http://our.api.com/Product : jika Anda ingin semua catatan

http://our.api.com/Product/<id1>,<id2> : seperti yang disarankan James dapat menjadi pilihan karena apa yang muncul setelah tag Produk adalah parameter

Atau yang paling saya sukai adalah:

Anda dapat menggunakan properti Hypermedia sebagai mesin status aplikasi (HATEOAS) dari RestFul WS dan melakukan panggilan http://our.api.com/Productyang akan mengembalikan URL setara http://our.api.com/Product/<id>dan memanggil mereka setelah ini.

Kedua

Ketika Anda harus melakukan pertanyaan pada panggilan url. Saya akan menyarankan menggunakan HATEOAS lagi.

1) Lakukan panggilan masuk ke http://our.api.com/term/pumas/productType/clothing/color/black

2) Lakukan panggilan masuk ke http://our.api.com/term/pumas/productType/clothing,bags/color/black,red

3) (Menggunakan HATEOAS) Lakukan panggilan ke ` http://our.api.com/term/pumas/productType/ -> terima url semua pakaian yang memungkinkan url -> panggil yang Anda inginkan (pakaian dan tas) - > terima kemungkinan url warna -> panggil yang Anda inginkan

Diego Dias
sumber
1
Saya dimasukkan ke dalam situasi yang sama beberapa hari yang lalu, Harus menyetel api istirahat (HATEOAS) untuk mendapatkan daftar objek yang disaring (besar) dan saya hanya memilih solusi kedua Anda. Bukankah mengingat api berulang-ulang untuk masing-masing sedikit berlebihan?
Samson
Itu benar-benar tergantung pada sistem Anda .... Jika itu sederhana dengan beberapa "opsi" mungkin harus berlebihan. Namun, jika Anda memiliki beberapa daftar yang sangat besar, dapat menjadi sangat merepotkan untuk melakukan semuanya dalam satu panggilan besar, selain itu jika API Anda bersifat publik, hal itu dapat menjadi rumit bagi pengguna (jika bersifat pribadi, seharusnya lebih mudah ... ajari pengguna yang Anda kenal). Sebagai alternatif, Anda dapat menerapkan kedua gaya, HATEOAS, dan panggilan "non-restful array" untuk pengguna tingkat lanjut
Diego Dias
Saya sedang membangun layanan web api yang tenang di rel dan saya harus mengikuti struktur url yang sama seperti di atas ( our.api.com/term/pumas/productType/clothing/color/black ). Tapi saya tidak yakin bagaimana mengkonfigurasi rute yang sesuai.
rubyist
Apakah istilah, jenis produk, dan warna adalah pengontrol Anda? Jika demikian, Anda hanya perlu melakukan: sumber daya: istilah lakukan sumber daya: productType lakukan sumber daya: color end end
Diego Dias
jenis produk dan warna adalah params. Jadi params dari productType adalah pakaian dan params dari pakaian adalah hitam
rubyist
12

Anda mungkin ingin memeriksa RFC 6570 . Spesifikasi Templat URI ini menunjukkan banyak contoh bagaimana URL dapat berisi parameter.

Darrel Miller
sumber
1
Bagian 3.2.8 tampaknya menjadi yang berlaku. Meskipun perlu dicatat bahwa ini hanya standar yang diusulkan dan tampaknya tidak bergerak melampaui titik itu.
Mike Post
3
@MikePost Sekarang IETF telah pindah ke proses jatuh tempo dua langkah untuk dokumen "track standar", saya berharap 6570 akan tetap seperti ini selama beberapa tahun lagi sebelum pindah ke "Standar Internet". tools.ietf.org/html/rfc6410 Spesifikasi ini sangat stabil, memiliki banyak implementasi dan digunakan secara luas.
Darrel Miller
Ah saya tidak menyadari perubahan itu. (Atau, TIL IETF sekarang lebih masuk akal.) Terima kasih!
Mike Post
8

Kasus pertama:

Pencarian produk normal akan terlihat seperti ini

http://our.api.com/product/1

Jadi saya berpikir bahwa praktik terbaik adalah bagi Anda untuk melakukan ini

http://our.api.com/Product/101404,7267261

Kasus kedua

Cari dengan parameter querystring - baik seperti ini. Saya akan tergoda untuk menggabungkan istilah dengan AND dan OR daripada menggunakan[] .

PS Ini bisa subjektif, jadi lakukan apa yang menurut Anda nyaman.

Alasan untuk menempatkan data di url adalah agar tautan dapat ditempel di situs / dibagi di antara pengguna. Jika ini bukan masalah, gunakan JSON / POST.

EDIT: Pada refleksi saya pikir pendekatan ini sesuai dengan suatu entitas dengan kunci majemuk, tetapi bukan permintaan untuk banyak entitas.

James Westgate
sumber
3
Tentu saja, dalam kedua contoh, jejak /tidak boleh ada karena URI membahas sumber daya, bukan koleksi.
Lawrence Dol
2
Saya selalu menggunakan kata kerja HTTP, dalam penggunaan REST adalah untuk melakukan tindakan tertentu, dan ini adalah garis panduan: GET: mengambil / membaca objek, POST membuat objek, PUT memperbarui objek yang ada, dan HAPUS menghapus objek. Jadi saya tidak akan menggunakan POST untuk mengambil sesuatu. Jika saya ingin daftar objek khususnya (filter), saya akan melakukan GET dengan daftar dalam parameter url (dipisahkan oleh koma, sepertinya baik)
Alex
1

Saya akan memihak jawaban nategood karena sudah lengkap dan sepertinya sudah menyenangkan kebutuhan Anda. Meskipun demikian, saya ingin menambahkan komentar tentang mengidentifikasi beberapa (1 atau lebih) sumber daya dengan cara itu:

http://our.api.com/Product/101404,7267261

Dengan demikian, Anda:

Komplekskan klien dengan memaksa mereka untuk menafsirkan respons Anda sebagai array, yang bagi saya kontra intuitif jika saya membuat permintaan berikut:http://our.api.com/Product/101404

Buat redundant APIs dengan satu API untuk mendapatkan semua produk dan yang di atas untuk mendapatkan 1 atau banyak. Karena Anda tidak boleh menampilkan lebih dari 1 halaman detail kepada pengguna demi UX, saya yakin memiliki lebih dari 1 ID akan sia-sia dan murni digunakan untuk memfilter produk.

Mungkin tidak bermasalah, tetapi Anda harus menangani sendiri sisi server ini dengan mengembalikan satu entitas (dengan memverifikasi apakah respons Anda mengandung satu atau lebih) atau membiarkan klien mengelolanya.

Contoh

Saya ingin memesan buku dari Amazing . Saya tahu persis buku mana itu dan saya melihatnya di daftar ketika menavigasi untuk buku-buku Horor:

  1. 10 000 garis luar biasa, 0 tes luar biasa
  2. Kembalinya monster yang menakjubkan
  3. Mari kita duplikat kode yang luar biasa
  4. Awal yang menakjubkan dari akhir

Setelah memilih buku kedua, saya diarahkan ke halaman yang merinci bagian buku dari daftar:

--------------------------------------------
Book #1
--------------------------------------------
    Title: The return of the amazing monster
    Summary:
    Pages:
    Publisher:
--------------------------------------------

Atau di halaman memberi saya rincian lengkap dari buku itu saja?

---------------------------------
The return of the amazing monster
---------------------------------
Summary:
Pages:
Publisher:
---------------------------------

Pendapat saya

Saya akan menyarankan menggunakan ID dalam variabel path ketika unicity dijamin ketika mendapatkan rincian sumber daya ini. Misalnya, API di bawah ini menyarankan beberapa cara untuk mendapatkan detail untuk sumber daya tertentu (dengan asumsi produk memiliki ID unik dan spesifikasi untuk produk tersebut memiliki nama unik dan Anda dapat menavigasi dari atas ke bawah):

/products/{id}
/products/{id}/specs/{name}

Saat Anda membutuhkan lebih dari 1 sumber daya, saya sarankan memfilter dari koleksi yang lebih besar. Untuk contoh yang sama:

/products?ids=

Tentu saja, ini pendapat saya karena tidak dipaksakan.

gumol
sumber