API tenang: kata kerja HTTP dengan URL bersama atau spesifik?
25
Saat membuat API RESTful , haruskah saya menggunakan kata kerja HTTP pada URL yang sama (bila memungkinkan) atau haruskah saya membuat URL spesifik per tindakan?
Sebagai contoh:
GET /items # Read all items
GET /items/:id # Read one item
POST /items # Create a new item
PUT /items/:id # Update one item
DELETE /items/:id # Delete one item
Atau dengan URL spesifik seperti:
GET /items # Read all items
GET /item/:id # Read one item
POST /items/new # Create a new item
PUT /item/edit/:id # Update one item
DELETE /item/delete/:id # Delete one item
Dalam skema terakhir Anda, Anda menyimpan kata kerja di URL sumber daya Anda. Ini harus dihindari karena kata kerja HTTP harus digunakan untuk tujuan itu. Rangkul protokol yang mendasarinya alih-alih mengabaikan, menduplikasi, atau menimpanya.
Lihat saja DELETE /item/delete/:id, Anda menempatkan informasi yang sama dua kali dalam permintaan yang sama. Ini berlebihan dan harus dihindari. Secara pribadi, saya akan bingung dengan ini. Apakah API benar-benar mendukung DELETEpermintaan? Bagaimana jika saya menempatkan deletedi URL dan menggunakan kata kerja HTTP yang berbeda? Apakah akan cocok dengan apa pun? Jika demikian, yang mana yang akan dipilih? Sebagai klien dari API yang dirancang dengan baik, saya tidak perlu mengajukan pertanyaan seperti itu.
Mungkin Anda membutuhkannya untuk mendukung klien yang tidak bisa mengeluarkan DELETEatau PUTmeminta. Jika itu masalahnya, saya akan meneruskan informasi ini di header HTTP. Beberapa API menggunakan X-HTTP-Method-Overrideheader untuk tujuan khusus ini (yang menurut saya cukup jelek). Saya tentu saja tidak akan menempatkan kata kerja di jalur.
Pergi untuk
GET /items # Read all items
GET /items/:id # Read one item
POST /items # Create a new item
PUT /items/:id # Update one item
DELETE /items/:id # Delete one item
Yang penting tentang kata kerja adalah bahwa kata kerja tersebut telah didefinisikan dengan baik dalam spesifikasi HTTP dan tetap sejalan dengan aturan ini memungkinkan Anda untuk menggunakan cache, proksi, dan mungkin alat lain di luar aplikasi Anda yang memahami semantik HTTP tetapi bukan semantik aplikasi Anda . Harap perhatikan bahwa alasan Anda harus menghindarinya di URL Anda bukan karena RESTful APIs yang membutuhkan URL yang dapat dibaca. Ini tentang menghindari ambiguitas yang tidak perlu.
Terlebih lagi, RESTful API dapat memetakan kata kerja ini (atau bagian daripadanya) ke perangkat semantik aplikasi apa pun, asalkan tidak bertentangan dengan spesifikasi HTTP. Sebagai contoh, sangat mungkin untuk membangun sebuah API tenang yang hanya menggunakan GET permintaan jika semua operasi yang memungkinkan adalah baik aman dan idempoten . Pemetaan di atas hanyalah contoh yang sesuai dengan kasus penggunaan Anda dan sesuai dengan spesifikasi. Tidak harus seperti ini.
Harap perhatikan juga bahwa API yang benar-benar RESTful seharusnya tidak pernah meminta programmer untuk membaca dokumentasi ekstensif dari URL yang tersedia selama Anda mematuhi prinsip HATEOAS (Hiperteks sebagai Mesin Aplikasi), yang merupakan salah satu asumsi inti dari REST . Tautan dapat benar-benar tidak dapat dipahami oleh manusia selama aplikasi klien dapat memahami dan menggunakannya untuk mengetahui kemungkinan transisi status aplikasi.
Dengan tidak adanya PUTdan DELETE, saya lebih suka menambahkannya ke jalur, tidak membedakannya dengan string kueri. Ini bukan modifikasi string kueri untuk operasi yang ada; ini adalah operasi terpisah.
Robert Harvey
4
@RobertHarvey dalam kasus ini, saya akan menyebutnya hack. Seperti yang Anda katakan, ini adalah operasi dan itu bukan sesuatu yang saya taruh di jalur ketika merancang API yang bertujuan untuk TETAP. Menempatkannya di string kueri sepertinya kurang invasif. Itu memang mencegah caching tetapi saya tidak berpikir respons terhadap permintaan semacam ini harus di-cache. Ini juga memungkinkan pengguna API untuk dengan mudah menunjukkan metode tanpa parsing atau membangun URL. Idealnya, API yang benar-benar TENANG harus menyediakan hyperlink tanpa mengharuskan klien membuat URL sendiri.
toniedzwiedz
Jika Anda tidak memiliki semua kata kerja, itu tidak sepenuhnya tenang, kan?
Robert Harvey
@RobertHarvey benar tapi saya menganggap ini sebagai mundur, bukan sebagai desain yang dimaksud. Saya membayangkan API harus mendukung metode HTTP aktual dan jika beberapa klien tidak dapat mengimplementasikannya karena alasan apa pun, mereka hanya dapat mengganti penggunaannya dengan params kueri ini. Proxy bahkan dapat mengambil ini dengan cepat dan mengubah permintaan menjadi yang menggunakan kata kerja HTTP asli sehingga server bahkan tidak perlu peduli. Beberapa API di sekitar benar-benar TENANG. Ketika datang ke API web umum, itu masalah selera. Secara pribadi, saya akan mencari URL yang bersih. Lebih mudah memahami IMHO.
toniedzwiedz
1
@RobertHarvey seperti yang dijelaskan, ini bukan cara yang dimaksudkan untuk menggunakannya. Saya hanya menemukan ini dua kejahatan yang lebih rendah ketika Anda harus mengatasi keterbatasan klien. Saya ingat pernah membaca dokumentasi untuk API seperti itu tetapi saya harus melakukan penggalian dalam riwayat browser / bookmark saya untuk menemukannya. Sekarang saya memikirkannya, header mungkin lebih baik dalam hal ini. Apakah kamu setuju?
toniedzwiedz
14
Yang pertama.
URI / URL adalah pengidentifikasi sumber daya (petunjuk dalam nama: pengidentifikasi sumber daya seragam). Dengan konvensi pertama, sumber daya yang dibicarakan ketika Anda melakukan "GET / user / 123" dan sumber daya yang dibicarakan ketika Anda melakukan "HAPUS / pengguna / 123" jelas sumber daya yang sama karena mereka memiliki URL yang sama.
Dengan konvensi kedua, Anda tidak dapat memastikan bahwa "GET / pengguna / 123" dan "HAPUS / pengguna / hapus / 123" sebenarnya adalah sumber daya yang sama, dan tampaknya menyiratkan bahwa Anda menghapus sumber daya terkait daripada sumber daya itu sendiri, jadi akan agak mengejutkan bahwa menghapus /user/delete/123sebenarnya menghapus /user/123. Jika Anda memiliki setiap operasi bekerja pada URL yang berbeda, URI tidak lagi berfungsi sebagai pengidentifikasi sumber daya.
Ketika Anda mengatakan DELETE /user/123, Anda mengatakan "hapus 'catatan pengguna dengan id 123'". Sementara jika Anda mengatakan DELETE /user/delete/123, apa yang tampaknya Anda maksudkan adalah "menghapus 'catatan penghapusan pengguna dengan id 123'", yang mungkin bukan yang ingin Anda katakan. Dan bahkan jika Anda menggunakan kata kerja yang lebih benar dalam situasi ini: "POST / user / delete / 123" yang mengatakan "lakukan operasi yang dilampirkan ke 'penghapusan pengguna dengan id 123'", itu masih merupakan cara bundaran untuk mengatakan menghapus catatan. (Ini mirip dengan nounifikasi kata kerja dalam bahasa Inggris).
Salah satu cara Anda berpikir tentang URL adalah memperlakukannya seperti pointer ke objek dan sumber daya sebagai objek dalam pemrograman berorientasi objek. Ketika Anda melakukan GET /user/123, DELETE /user/123, Anda bisa memikirkan menganggap mereka sebagai metode dalam objek: [/user/123].get(), [/user/123].delete()di mana []adalah seperti pointer dereferencing Operator tapi untuk URL (jika Anda tahu bahasa yang memiliki pointer). Salah satu prinsip yang mendasari REST adalah antarmuka yang seragam, yaitu memiliki seperangkat kata kerja / metode yang kecil dan terbatas yang berfungsi untuk segala sesuatu dalam jaringan sumber daya / objek yang luas.
Karena itu, yang pertama lebih baik.
PS: tentu saja ini melihat REST dengan cara yang paling murni. Terkadang kepraktisan mengalahkan kemurnian, dan Anda perlu membuat konsesi untuk klien atau kerangka kerja yang membuat otak sulit untuk melakukan REST yang tepat.
(maaf, pertama kali saya melalui saya melewatkan / edit / dan / delete / in (2) ...)
Gagasan URI adalah bahwa itu adalah pengidentifikasi sumber daya yang dapat dialamatkan , bukan doa metode . Jadi URI harus menunjuk ke sumber daya tertentu. Dan jika Anda menghormati URI, Anda harus selalu mendapatkan sumber daya yang sama.
Artinya, Anda harus memikirkan URI dengan cara yang sama seperti Anda memikirkan Kunci Utama dari sebuah baris dalam basis data. Ini secara unik mengidentifikasi sesuatu: Pengidentifikasi Sumber Daya Universal.
Jadi, apakah Anda menggunakan jamak atau tunggal, URI harus menjadi pengidentifikasi daripada doa . Apa yang Anda coba lakukan ada dalam metode ini, yaitu: DAPATKAN (dapatkan), PUT (buat / perbarui), HAPUS (hapus) atau POST (semuanya).
Jadi "/ item / delete / 123" istirahat REST karena itu tidak menunjuk ke sumber daya, itu lebih merupakan doa metode.
(Juga, secara semantik, Anda harus dapat MENDAPATKAN URI, memutuskan itu sudah ketinggalan zaman, dan kemudian HAPUS URI yang sama - karena itu adalah pengidentifikasi. Jika GET URI tidak memiliki "/ delete /" dan DELETE melakukannya, maka itu bertentangan dengan semantik HTTP. Anda menyiarkan 2 atau lebih URI per sumber daya tempat 1 akan melakukannya.)
Sekarang, cheatnya adalah ini: tidak ada definisi nyata yang jelas tentang apa yang bisa dan bukan sumber daya, jadi penghindaran yang umum di REST adalah untuk mendefinisikan "kata benda pemrosesan" dan arahkan URI ke sana. Itu cukup banyak permainan kata, tetapi memuaskan semantik.
Jadi, jika, misalnya, Anda benar-benar tidak dapat menggunakan ini karena beberapa alasan:
DELETE /items/123
Anda dapat mendeklarasikan kepada dunia bahwa Anda memiliki sumber daya pemrosesan dan penggunaan "penghapusan"
POST /items/deletor { id: 123 }
Sekarang, itu sangat mirip dengan RPC (Remote Procedure Call), tetapi jatuh melalui celah besar klausa "pemrosesan data" spesifikasi POST yang disebutkan dalam spesifikasi HTTP.
Namun, melakukan itu agak luar biasa, dan jika Anda dapat menggunakan PUT umum untuk membuat / memperbarui, HAPUS untuk menghapus, dan POST untuk menambahkan, membuat, dan segala sesuatu yang lain, maka Anda harus melakukannya , karena itu adalah penggunaan HTTP yang lebih standar. Tetapi jika Anda memiliki case rumit seperti "commit" atau "publish" atau "redact", maka case untuk menggunakan kata benda prosesor memenuhi purest REST dan masih memberi Anda semantik yang Anda butuhkan.
PUT
danDELETE
, saya lebih suka menambahkannya ke jalur, tidak membedakannya dengan string kueri. Ini bukan modifikasi string kueri untuk operasi yang ada; ini adalah operasi terpisah.Yang pertama.
URI / URL adalah pengidentifikasi sumber daya (petunjuk dalam nama: pengidentifikasi sumber daya seragam). Dengan konvensi pertama, sumber daya yang dibicarakan ketika Anda melakukan "GET / user / 123" dan sumber daya yang dibicarakan ketika Anda melakukan "HAPUS / pengguna / 123" jelas sumber daya yang sama karena mereka memiliki URL yang sama.
Dengan konvensi kedua, Anda tidak dapat memastikan bahwa "GET / pengguna / 123" dan "HAPUS / pengguna / hapus / 123" sebenarnya adalah sumber daya yang sama, dan tampaknya menyiratkan bahwa Anda menghapus sumber daya terkait daripada sumber daya itu sendiri, jadi akan agak mengejutkan bahwa menghapus
/user/delete/123
sebenarnya menghapus/user/123
. Jika Anda memiliki setiap operasi bekerja pada URL yang berbeda, URI tidak lagi berfungsi sebagai pengidentifikasi sumber daya.Ketika Anda mengatakan
DELETE /user/123
, Anda mengatakan "hapus 'catatan pengguna dengan id 123'". Sementara jika Anda mengatakanDELETE /user/delete/123
, apa yang tampaknya Anda maksudkan adalah "menghapus 'catatan penghapusan pengguna dengan id 123'", yang mungkin bukan yang ingin Anda katakan. Dan bahkan jika Anda menggunakan kata kerja yang lebih benar dalam situasi ini: "POST / user / delete / 123" yang mengatakan "lakukan operasi yang dilampirkan ke 'penghapusan pengguna dengan id 123'", itu masih merupakan cara bundaran untuk mengatakan menghapus catatan. (Ini mirip dengan nounifikasi kata kerja dalam bahasa Inggris).Salah satu cara Anda berpikir tentang URL adalah memperlakukannya seperti pointer ke objek dan sumber daya sebagai objek dalam pemrograman berorientasi objek. Ketika Anda melakukan
GET /user/123
,DELETE /user/123
, Anda bisa memikirkan menganggap mereka sebagai metode dalam objek:[/user/123].get()
,[/user/123].delete()
di mana[]
adalah seperti pointer dereferencing Operator tapi untuk URL (jika Anda tahu bahasa yang memiliki pointer). Salah satu prinsip yang mendasari REST adalah antarmuka yang seragam, yaitu memiliki seperangkat kata kerja / metode yang kecil dan terbatas yang berfungsi untuk segala sesuatu dalam jaringan sumber daya / objek yang luas.Karena itu, yang pertama lebih baik.
PS: tentu saja ini melihat REST dengan cara yang paling murni. Terkadang kepraktisan mengalahkan kemurnian, dan Anda perlu membuat konsesi untuk klien atau kerangka kerja yang membuat otak sulit untuk melakukan REST yang tepat.
sumber
(maaf, pertama kali saya melalui saya melewatkan / edit / dan / delete / in (2) ...)
Gagasan URI adalah bahwa itu adalah pengidentifikasi sumber daya yang dapat dialamatkan , bukan doa metode . Jadi URI harus menunjuk ke sumber daya tertentu. Dan jika Anda menghormati URI, Anda harus selalu mendapatkan sumber daya yang sama.
Artinya, Anda harus memikirkan URI dengan cara yang sama seperti Anda memikirkan Kunci Utama dari sebuah baris dalam basis data. Ini secara unik mengidentifikasi sesuatu: Pengidentifikasi Sumber Daya Universal.
Jadi, apakah Anda menggunakan jamak atau tunggal, URI harus menjadi pengidentifikasi daripada doa . Apa yang Anda coba lakukan ada dalam metode ini, yaitu: DAPATKAN (dapatkan), PUT (buat / perbarui), HAPUS (hapus) atau POST (semuanya).
Jadi "/ item / delete / 123" istirahat REST karena itu tidak menunjuk ke sumber daya, itu lebih merupakan doa metode.
(Juga, secara semantik, Anda harus dapat MENDAPATKAN URI, memutuskan itu sudah ketinggalan zaman, dan kemudian HAPUS URI yang sama - karena itu adalah pengidentifikasi. Jika GET URI tidak memiliki "/ delete /" dan DELETE melakukannya, maka itu bertentangan dengan semantik HTTP. Anda menyiarkan 2 atau lebih URI per sumber daya tempat 1 akan melakukannya.)
Sekarang, cheatnya adalah ini: tidak ada definisi nyata yang jelas tentang apa yang bisa dan bukan sumber daya, jadi penghindaran yang umum di REST adalah untuk mendefinisikan "kata benda pemrosesan" dan arahkan URI ke sana. Itu cukup banyak permainan kata, tetapi memuaskan semantik.
Jadi, jika, misalnya, Anda benar-benar tidak dapat menggunakan ini karena beberapa alasan:
Anda dapat mendeklarasikan kepada dunia bahwa Anda memiliki sumber daya pemrosesan dan penggunaan "penghapusan"
Sekarang, itu sangat mirip dengan RPC (Remote Procedure Call), tetapi jatuh melalui celah besar klausa "pemrosesan data" spesifikasi POST yang disebutkan dalam spesifikasi HTTP.
Namun, melakukan itu agak luar biasa, dan jika Anda dapat menggunakan PUT umum untuk membuat / memperbarui, HAPUS untuk menghapus, dan POST untuk menambahkan, membuat, dan segala sesuatu yang lain, maka Anda harus melakukannya , karena itu adalah penggunaan HTTP yang lebih standar. Tetapi jika Anda memiliki case rumit seperti "commit" atau "publish" atau "redact", maka case untuk menggunakan kata benda prosesor memenuhi purest REST dan masih memberi Anda semantik yang Anda butuhkan.
sumber