REST API Praktik terbaik: Di mana harus meletakkan parameter? [Tutup]

348

API REST dapat memiliki parameter setidaknya dalam dua cara:

  1. Sebagai bagian dari jalur URL (yaitu /api/resource/parametervalue )
  2. Sebagai argumen kueri (yaitu /api/resource?parameter=value )

Apa praktik terbaik di sini? Apakah ada pedoman umum kapan menggunakan 1 dan kapan menggunakan 2?

Contoh dunia nyata: Twitter menggunakan parameter kueri untuk menentukan interval. ( http://api.twitter.com/1/statuses/home_timeline.json?since_id=12345&max_id=54321)

Apakah akan dianggap desain yang lebih baik untuk menempatkan parameter ini di jalur URL?

Kalle Gustafsson
sumber

Jawaban:

254

Jika ada praktik terbaik yang terdokumentasi, saya belum menemukannya. Namun, berikut adalah beberapa panduan yang saya gunakan saat menentukan tempat untuk meletakkan parameter dalam url:

Parameter opsional cenderung lebih mudah dimasukkan ke dalam string kueri.

Jika Anda ingin mengembalikan kesalahan 404 ketika nilai parameter tidak sesuai dengan sumber daya yang ada maka saya akan cenderung menuju parameter segmen jalur. mis. di /customer/232mana 232 bukan id pelanggan yang valid.

Namun jika Anda ingin mengembalikan daftar kosong maka ketika parameter tidak ditemukan maka saya sarankan menggunakan parameter string kueri. misalnya/contacts?name=dave

Jika parameter memengaruhi seluruh subtree ruang URI Anda, maka gunakan segmen jalur. misalnya parameter bahasa /en/document/foo.txt versus/document/foo.txt?language=en

Saya lebih suka pengidentifikasi unik berada di segmen jalur daripada parameter kueri.

Aturan resmi untuk URI ditemukan dalam spesifikasi RFC ini di sini . Ada juga spesifikasi RFC lain yang sangat berguna di sini yang mendefinisikan aturan untuk parameterisasi URI.

Darrel Miller
sumber
5
Aturan resmi URI dan rancangan sepc benar-benar bermanfaat & menarik! :-)
KajMagnus
1
Tes kesalahan 404 banyak membantu saya untuk menghindari memasukkan informasi ke jalur yang termasuk dalam parameter kueri, header, atau badan permintaan. Terima kasih untuk menunjukkan itu!
Kevin Condon
152

Jawaban terlambat tetapi saya akan menambahkan beberapa wawasan tambahan untuk apa yang telah dibagikan, yaitu bahwa ada beberapa jenis "parameter" untuk permintaan, dan Anda harus mempertimbangkan ini.

  1. Pencari Lokasi - Misalnya pengidentifikasi sumber daya seperti ID atau tindakan / tampilan
  2. Filter - Misalnya parameter yang menyediakan pencarian, mengurutkan atau mempersempit kumpulan hasil.
  3. State - Misalnya identifikasi sesi, kunci api, whatevs.
  4. Konten - Misalnya data yang akan disimpan.

Sekarang mari kita lihat tempat-tempat berbeda di mana parameter ini bisa pergi.

  1. Minta tajuk & cookie
  2. String kueri URL ("GET" vars)
  3. Jalur URL
  4. String kueri tubuh / multipart ("POST" vars)

Secara umum Anda ingin Negara diatur dalam tajuk atau cookie, tergantung pada jenis informasi negara itu. Saya pikir kita semua bisa sepakat tentang ini. Gunakan header http khusus (X-My-Header) jika perlu.

Demikian pula, Konten hanya memiliki satu tempat untuk dimiliki, yang ada di badan permintaan, baik sebagai string kueri atau sebagai multipart http dan / atau konten JSON. Ini konsisten dengan apa yang Anda terima dari server ketika mengirimkan konten kepada Anda. Jadi Anda tidak boleh kasar dan melakukannya secara berbeda.

Penelusur seperti "id = 5" atau "action = refresh" atau "halaman = 2" akan masuk akal untuk memiliki sebagai jalur URL, seperti di mysite.com/article/5/page=2mana sebagian Anda tahu arti setiap bagian (dasar-dasar seperti artikel dan 5 jelas berarti memberi saya data artikel jenis dengan id 5) dan parameter tambahan ditentukan sebagai bagian dari URI. Mereka bisa dalam bentuk page=2, atau page/2jika Anda tahu bahwa setelah titik tertentu di URI "folder" dipasangkan nilai kunci.

Filter selalu masuk dalam string kueri, karena meskipun mereka adalah bagian dari menemukan data yang tepat, mereka hanya ada untuk mengembalikan subset atau modifikasi dari apa yang dikembalikan oleh Locator sendirian. Pencarian dalam mysite.com/article/?query=Obama(subset) adalah filter, dan begitu juga/article/5?order=backwards (modifikasi). Pikirkan tentang apa yang dilakukannya, bukan hanya apa namanya!

Jika "view" menentukan format output, maka itu adalah filter ( mysite.com/article/5?view=pdf) karena ia mengembalikan modifikasi dari sumber yang ditemukan alih-alih menggunakan sumber daya yang kita inginkan. Jika itu malah memutuskan bagian mana dari artikel yang bisa kita lihat (mysite.com/article/5/view=summary ) maka itu adalah locator.

Ingat, persempit sumber daya adalah penyaringan. Menemukan sesuatu yang spesifik dalam suatu sumber berarti menemukan ... ya. Pemfilteran subset dapat mengembalikan sejumlah hasil (bahkan 0). Menemukan akan selalu menemukan contoh spesifik dari sesuatu (jika ada). Pemfilteran modifikasi akan mengembalikan data yang sama dengan locator, kecuali dimodifikasi (jika modifikasi seperti itu diizinkan).

Semoga ini membantu memberi orang-orang beberapa momen eureka jika mereka bingung tentang di mana harus meletakkan barang-barang!

Tor Valamo
sumber
2
Mengapa bukan idfilter? Ini mengembalikan sebagian dari sumber daya
Jonathan.
13
@ Jonathan. tidak itu mengembalikan sumber daya tertentu, yaitu artikel nomor 5. Filter selalu merupakan cara untuk mempersempit pencarian dalam kumpulan sumber daya. Jika Anda hanya menginginkan sumber daya tertentu, maka harus ada cara yang ditunjuk untuk mendapatkannya. Penyaringan berarti Anda memiliki kemungkinan untuk mengembalikan beberapa sumber daya. ID bukan filter, itu adalah sumber daya tunggal yang pasti. Jika Anda memiliki RANGE of IDs, maka itu akan menjadi filter, bahkan jika rentangnya hanya menyertakan satu ID. Jika filter juga menyertakan jenis sumber daya, itu akan mengembalikan semua sumber daya dengan ID 5, bukan hanya artikel.
Tor Valamo
1
@ Jonathan: seperti DarrelMiller yang disebutkan, Anda akan mengharapkan permintaan pada objek / id untuk mengembalikan 404 jika id tidak diketahui, sedangkan Anda akan mengharapkan objek? Id = id untuk kembali dan daftar kosong. Juga, saya akan mempertimbangkan bahwa segala jenis penyaringan / subset harus mengembalikan daftar.
njzk2
1
Halaman adalah yang sulit, karena seperti yang Anda katakan itu bisa menjadi filter dari sumber daya (kumpulan halaman), tetapi kemudian pada saat yang sama itu adalah sumber daya spesifik dalam koleksi itu. Saya akan selalu meminta halaman artikel dengan locator, bukan filter. Namun, halaman tersebut dapat menjadi filter dari daftar sesuatu, katakanlah daftar pengguna. Tapi kemudian halaman secara inheren pembatas, alias "mulai dari item (page-1)*perpagedan tampilkan perpageitem". Menggunakannya sebagai filter sudah benar, tetapi untuk alasan yang berbeda. Menyebutnya "halaman" secara teknis salah. Lebih semantik benar akan menyebutnya "dari" atau "startAt"
Tor Valamo
1
(lanjutan) Arti semantik dari "halaman" adalah bahwa ia adalah sumber daya spesifik yang tidak berubah. Itu berasal dari cetakan fisik. Jika kita tidak pernah memiliki buku atau barang cetakan, "halaman" tidak akan benar-benar sepatah kata pun. Jika Anda memiliki daftar item yang dinamis, dipecah menjadi "halaman", Anda harus benar-benar memberikan titik awal yang spesifik, baik numerik, alfabetis atau bahkan item-spesifik, serta filter "berapa banyak per halaman". Jika saya ingin merujuk sesuatu dalam daftar Anda, saya ingin spesifik. Juga saya tidak ingin pergi ke halaman 5 hanya untuk menyadari bahwa Anda sekarang telah mengubah internal perpagemenjadi 50 bukannya 20.
Tor Valamo
21

Itu tergantung pada desain. Tidak ada aturan untuk URI di REST melalui HTTP (hal utama adalah mereka unik). Seringkali menyangkut masalah rasa dan intuisi ...

Saya mengambil pendekatan berikut:

  • url path-element: Sumber daya dan elemen pathnya membentuk traversal direktori dan sub-sumber daya (mis. / items / {id}, / users / items). Ketika tidak yakin bertanya pada kolega Anda, apakah mereka berpikir traversal itu dan mereka berpikir dalam "direktori lain" kemungkinan besar path-element adalah pilihan yang tepat
  • parameter url: ketika benar-benar tidak ada traversal (sumber daya pencarian dengan beberapa parameter kueri adalah contoh yang sangat bagus untuk itu)
manuel aldana
sumber
1
Sebenarnya ada aturan yang cukup jelas tentang bagaimana seharusnya URI terlihat, dan sangat sedikit ambiguitas tentang bagaimana menerapkannya pada URI tenang.
DanMan
18

IMO parameter harus lebih baik sebagai argumen kueri. Url digunakan untuk mengidentifikasi sumber daya, sedangkan parameter kueri ditambahkan untuk menentukan bagian sumber daya yang Anda inginkan, status apa pun yang harus dimiliki sumber daya, dll.

PeterWong
sumber
7
Sebenarnya, jalur dan kueri digunakan dalam kombinasi untuk mengidentifikasi sumber daya. Ini diklarifikasi dalam RFC 3986 http://labs.apache.org/webarch/uri/rfc/rfc3986.html#query
Darrel Miller
@DarrelMiller Saya tahu ini adalah posting lama tapi saya tertarik untuk mengetahui lebih banyak tentang parameter kueri fakta yang juga digunakan untuk mengidentifikasi sumber daya. Tautan yang Anda berikan sekarang mati. Saya telah melihat RFC3986 tetapi saya tidak melihat bagaimana Anda menyimpulkan fakta ini. Juga, menurut definisi, parameter pengidentifikasi tidak boleh opsional sehingga tampaknya tidak sesuai untuk menggunakan parameter kueri untuk identifikasi.
Mickael Marrache
@MickaelMarrache Lihat baris pertama di bagian 3.4 tools.ietf.org/html/rfc3986#section-3.4
Darrel Miller
2
@DarrelMiller Terima kasih! Pertanyaan saya berasal dari fakta bahwa secara umum, komponen HTTP perantara tidak menembolok respons permintaan yang berisi string kueri. Jadi, tampaknya parameter kueri lebih sesuai untuk mencari sumber daya menurut beberapa kriteria dan tidak secara unik mengidentifikasi sumber daya.
Mickael Marrache
17

Sesuai Implementasi REST,

1) Variabel path digunakan untuk aksi langsung pada sumber daya, seperti kontak atau lagu ex ..
GET dll / api / resource / {songid} atau
GET etc / api / resource / {contactid} akan mengembalikan data masing-masing.

2) Perms / argumen permintaan digunakan untuk sumber daya tidak langsung seperti metadata lagu ex .., GET / api / resource / {songid}? Metadata = genre itu akan mengembalikan data genre untuk lagu tertentu.

Satish Bellapu
sumber
5
Sebenarnya tidak ada standar REST . Per Wikipedia : Tidak seperti layanan web berbasis SOAP, tidak ada standar "resmi" untuk API web RESTful. [14] Ini karena REST adalah gaya arsitektur, tidak seperti SOAP, yang merupakan protokol. Meskipun REST bukan standar, implementasi RESTful seperti Web dapat menggunakan standar seperti HTTP, URI, XML, dll.
DavidRR
Saya tidak suka pendekatan 2. Saya lebih suka memilih / api / genre? Songid = 123 atau / api / lagu / {song-id} / genre
Bart Calixto
1
@ Bart, Satish mengacu pada Variabel di jalur, yang pada dasarnya adalah apa yang Anda rujuk sebagai preferensi Anda .. Namun, jika genre sebenarnya metadata, dan bukan bidang entitas lagu / sumber daya .. maka saya bisa melihat lebih banyak sensibilitas dalam menggunakan string kueri di atasnya ..
Brett Caswell
@ BrettCaswell mengerti! terima kasih telah menunjukkannya. sangat menghargai itu!
Bart Calixto
16

"Kemas" dan POST data Anda terhadap "konteks" yang disediakan oleh pencari sumber daya-alam semesta, yang berarti # 1 untuk kepentingan pencari lokasi.

Pikirkan keterbatasan dengan # 2. Saya lebih suka POST ke # 1.

catatan: batasan dibahas untuk

POST dalam Apakah ada ukuran maksimal untuk konten parameter POST?

DAPATKAN Apakah ada batasan untuk panjang permintaan GET? dan Ukuran maksimum parameter URL di _GET

ps batas ini didasarkan pada kemampuan klien (browser) dan server (konfigurasi).

dgm
sumber
add-on: rute cerdas dapat memiliki versi (dibedakan melalui header) sehingga memberikan fungsionalitas yang dikembangkan tanpa perlu mengubah kode yang menggunakan kode rest-full (api) yang Anda tulis seperti dalam restify -> cari Versioned Routes
dgm
5

Menurut standar URI path adalah untuk parameter hirarkis dan kueri untuk parameter non-hirarkis. Ofc. itu bisa sangat subjektif apa yang hirarkis untuk Anda.

Dalam situasi di mana banyak URI ditugaskan ke sumber daya yang sama, saya ingin meletakkan parameter - yang diperlukan untuk identifikasi - ke jalur dan parameter - yang diperlukan untuk membangun representasi - ke dalam kueri. (Bagi saya dengan cara ini lebih mudah untuk rute.)

Sebagai contoh:

  • /users/123 dan /users/123?fields="name, age"
  • /users dan /users?name="John"&age=30

Untuk pengurangan peta, saya suka menggunakan pendekatan berikut:

  • /users?name="John"&age=30
  • /users/name:John/age:30

Jadi terserah Anda (dan router sisi server Anda) bagaimana Anda membuat URI.

Catatan: Hanya menyebutkan parameter ini adalah parameter permintaan. Jadi apa yang sebenarnya Anda lakukan adalah mendefinisikan bahasa permintaan sederhana. Dengan kueri yang rumit (yang berisi operator seperti dan, atau, lebih besar dari, dll.) Saya sarankan Anda untuk menggunakan bahasa permintaan yang sudah ada. Kemampuan templat URI sangat terbatas ...

inf3rno
sumber
4

Sebagai seorang programmer yang sering berada di sisi klien, saya lebih suka argumen kueri. Juga, bagi saya, ini memisahkan jalur URL dari parameter, menambah kejelasan, dan menawarkan ekstensibilitas yang lebih besar. Ini juga memungkinkan saya untuk memiliki logika terpisah antara bangunan URL / URI dan pembangun parameter.

Saya suka apa yang dikatakan manuel aldana tentang opsi lain jika ada semacam pohon yang terlibat. Saya dapat melihat bagian-bagian khusus pengguna yang ditata seperti itu.

Joe Plante
sumber
4

Tidak ada aturan yang keras dan cepat, tetapi aturan praktis dari sudut pandang konseptual murni yang ingin saya gunakan dapat diringkas secara singkat seperti ini: jalur URI (menurut definisi) mewakili sumber daya dan parameter kueri pada dasarnya merupakan pengubah pada sumber daya itu . Sejauh yang kemungkinan tidak membantu ... Dengan REST API Anda memiliki metode utama bertindak atas sumber daya tunggal menggunakan GET, PUTdan DELETE. Oleh karena itu apakah sesuatu harus diwakili di jalur atau sebagai parameter dapat dikurangi menjadi apakah metode tersebut masuk akal untuk representasi yang dimaksud. Apakah Anda secara wajar melakukan PUTsesuatu di jalur itu dan apakah secara semantik terdengar seperti itu? Anda tentu saja dapat melakukan PUTsesuatu di mana saja dan menekuk bagian belakang untuk menanganinya, tetapi Anda harus melakukannyaPUTing apa yang berarti representasi dari sumber daya aktual dan bukan versi yang tidak perlu dikontekstualisasikan darinya. Untuk koleksi yang sama bisa dilakukan dengan POST. Jika Anda ingin menambahkan ke koleksi tertentu apa yang akan menjadi URL yang masuk akal POSTuntuk.

Ini masih menyisakan beberapa area abu-abu karena beberapa jalur dapat menunjukkan berapa jumlah sumber daya orang tua yang agak tergantung pada penggunaannya. Satu garis keras yang menarik adalah bahwa semua jenis representasi transitif harus dilakukan menggunakan parameter kueri, karena tidak akan memiliki sumber daya yang mendasarinya.

Menanggapi contoh dunia nyata yang diberikan dalam pertanyaan asli (API Twitter), parameter mewakili kueri transitif yang memfilter keadaan sumber daya (bukan hierarki). Dalam contoh khusus itu akan sepenuhnya tidak masuk akal untuk menambah koleksi yang diwakili oleh kendala-kendala itu, dan lebih jauh bahwa kueri tidak akan dapat direpresentasikan sebagai jalur yang akan masuk akal dalam hal grafik objek.

Adopsi jenis perspektif berorientasi sumber daya ini dapat dengan mudah memetakan langsung ke grafik objek dari model domain Anda dan mengarahkan logika API Anda ke titik di mana semuanya bekerja dengan sangat bersih dan dengan cara mendokumentasikan diri sendiri begitu jelas. Konsep ini juga dapat dibuat lebih jelas dengan menjauh dari sistem yang menggunakan perutean URL tradisional yang dipetakan ke model data yang biasanya tidak pas (yaitu RDBMS). Apache Sling pasti akan menjadi tempat yang baik untuk memulai. Konsep pengiriman objek traversal dalam sistem seperti Zope juga menyediakan analog yang lebih jelas.

Matt Whipple
sumber
4

Ini pendapat saya.

Params kueri digunakan sebagai data meta untuk suatu permintaan. Mereka bertindak sebagai filter atau pengubah panggilan sumber daya yang ada.

Contoh:

/calendar/2014-08-08/events

harus memberikan acara kalender untuk hari itu.

Jika Anda ingin acara untuk kategori tertentu

/calendar/2014-08-08/events?category=appointments

atau jika Anda membutuhkan acara yang lebih dari 30 menit

/calendar/2014-08-08/events?duration=30

Tes lakmus akan memeriksa apakah permintaan masih dapat dilayani tanpa params permintaan.

Jay
sumber
2

Saya biasanya cenderung ke arah # 2, Sebagai argumen kueri (yaitu / api / resource? Parameter = value).

Opsi ketiga adalah benar-benar memposting parameter = nilai dalam tubuh.

Ini karena ia berfungsi lebih baik untuk sumber daya multi-parameter dan lebih dapat diperluas untuk penggunaan di masa mendatang.

Tidak peduli yang mana yang Anda pilih, pastikan Anda hanya memilih satu, jangan mencampur dan mencocokkan. Itu mengarah ke API yang membingungkan.

NorthIsUp
sumber
2

Satu "dimensi" dari topik ini telah ditinggalkan namun ini sangat penting: ada saatnya "praktik terbaik" harus dibuat sesuai dengan plaform yang kami laksanakan atau ditambahi dengan kemampuan REST.

Contoh praktis:

Banyak aplikasi web saat ini mengimplementasikan arsitektur MVC (Model, View, Controller). Mereka menganggap jalur standar tertentu disediakan, terlebih lagi ketika aplikasi web itu datang dengan opsi "Aktifkan SEO URL".

Untuk menyebutkan aplikasi web yang cukup terkenal: toko e-commerce OpenCart. Ketika admin mengaktifkan "SEO SEO", ia mengharapkan URL tersebut dalam format MVC yang cukup standar seperti:

http://www.domain.tld/special-offers/list-all?limit=25

Dimana

  • special-offers adalah pengontrol MVC yang akan memproses URL (menampilkan halaman penawaran khusus)

  • list-alladalah tindakan atau nama fungsi pengendali untuk memanggil. (*)

  • limit = 25 adalah opsi, yang menyatakan bahwa 25 item akan ditampilkan per halaman.

(*) list-alladalah nama fungsi fiktif yang saya gunakan untuk kejelasan. Pada kenyataannya, OpenCart dan sebagian besar kerangka kerja MVC memiliki fungsi default, tersirat (dan biasanya dihilangkan dalam URL) indexyang dipanggil ketika pengguna menginginkan tindakan default untuk dilakukan. Jadi URL dunia nyata adalah:

http://www.domain.tld/special-offers?limit=25

Dengan struktur aplikasi atau kerangka kerja yang sekarang cukup standar mirip dengan di atas, Anda akan sering mendapatkan server web yang dioptimalkan untuk itu, yang menulis ulang URL untuk itu ("URL bukan SEO" sebenarnya adalah:) http://www.domain.tld/index.php?route=special-offers/list-all&limit=25.

Oleh karena itu Anda, sebagai pengembang, dihadapkan pada berurusan dengan infrastruktur yang ada dan mengadaptasi "praktik terbaik" Anda, kecuali jika Anda adalah admin sistem, tahu persis bagaimana men-tweak konfigurasi penulisan ulang Apache / NGinx (yang terakhir dapat menjadi jahat!) Dan sebagainya di.

Jadi, API REST Anda seringkali jauh lebih baik mengikuti standar aplikasi web rujukan, baik untuk konsistensi dan kemudahan / kecepatan (dan dengan demikian menghemat anggaran).

Untuk kembali ke contoh praktis di atas, REST API yang konsisten akan menjadi sesuatu dengan URL seperti:

http://www.domain.tld/api/special-offers-list?from=15&limit=25

atau (bukan URL SEO)

http://www.domain.tld/index.php?route=api/special-offers-list?from=15&limit=25

dengan campuran argumen "jalur terbentuk" dan argumen "kueri terbentuk".

Dario Fumagalli
sumber
1

Saya melihat banyak API REST yang tidak menangani parameter dengan baik. Salah satu contoh yang sering muncul adalah ketika URI memasukkan informasi yang dapat diidentifikasi secara pribadi.

http://software.danielwatrous.com/design-principles-for-rest-apis/

Saya pikir pertanyaan wajar adalah ketika sebuah parameter seharusnya tidak menjadi parameter sama sekali, tetapi sebaliknya harus dipindahkan ke HEADER atau BODY dari permintaan.

Daniel Watrous
sumber
0

Itu pertanyaan yang sangat menarik.

Anda dapat menggunakan keduanya, tidak ada aturan ketat tentang subjek ini, tetapi menggunakan variabel path URI memiliki beberapa keunggulan:

  • Cache : Sebagian besar layanan cache web di internet tidak men-cache permintaan GET saat mengandung parameter kueri. Mereka melakukan itu karena ada banyak sistem RPC menggunakan permintaan GET untuk mengubah data di server (gagal !! Dapatkan harus menjadi metode yang aman)

Tetapi jika Anda menggunakan variabel jalur, semua layanan ini dapat menembolok permintaan GET Anda.

  • Hierarki : Variabel path dapat mewakili hierarki: / City / Street / Place

Ini memberi pengguna informasi lebih lanjut tentang struktur data.

Tetapi jika data Anda tidak memiliki hubungan hierarki apa pun, Anda masih dapat menggunakan variabel Path, menggunakan koma atau semi-kolon:

/ Kota / bujur, lintang

Sebagai aturan, gunakan koma ketika memesan parameter penting, gunakan semi-kolon ketika pemesanan tidak masalah:

/ IconGenerator / merah; biru; hijau

Terlepas dari alasan itu, ada beberapa kasus ketika sangat umum untuk menggunakan variabel string kueri:

  • Ketika Anda membutuhkan browser untuk secara otomatis memasukkan variabel formulir HTML ke dalam URI
  • Ketika Anda berurusan dengan algoritma. Misalnya mesin google menggunakan string kueri:

http: // www.google.com/search?q=rest

Singkatnya, tidak ada alasan kuat untuk menggunakan salah satu metode ini tetapi kapan pun Anda bisa, gunakan variabel URI.

jfcorugedo
sumber