Alternatif RESTful untuk HAPUS Badan Permintaan

95

Meskipun spesifikasi HTTP 1.1 tampaknya mengizinkan badan pesan pada permintaan DELETE , tampaknya menunjukkan bahwa server harus mengabaikannya karena tidak ada semantik yang ditentukan untuk itu.

4.3 Isi Pesan

Server HARUS membaca dan meneruskan badan pesan berdasarkan permintaan apa pun; jika metode permintaan tidak menyertakan semantik yang ditentukan untuk badan entitas, maka badan pesan HARUS diabaikan saat menangani permintaan.

Saya telah meninjau beberapa diskusi terkait tentang topik ini di SO dan seterusnya, seperti:

Sebagian besar diskusi tampaknya setuju bahwa memberikan badan pesan pada HAPUS mungkin diperbolehkan , tetapi umumnya tidak disarankan.

Lebih lanjut, saya telah memperhatikan tren di berbagai pustaka klien HTTP di mana semakin banyak peningkatan tampaknya dicatat untuk pustaka ini untuk mendukung badan permintaan di DELETE. Sebagian besar perpustakaan tampaknya menurut, meskipun kadang-kadang dengan sedikit penolakan awal.

Kasus penggunaan saya meminta penambahan beberapa metadata yang diperlukan pada DELETE (misalnya "alasan" untuk penghapusan, bersama dengan beberapa metadata lain yang diperlukan untuk penghapusan). Saya telah mempertimbangkan opsi berikut, tidak ada yang tampak sepenuhnya sesuai dan sejalan dengan spesifikasi HTTP dan / atau praktik terbaik REST:

  • Isi Pesan - Spesifikasi menunjukkan bahwa badan pesan di DELETE tidak memiliki nilai semantik; tidak sepenuhnya didukung oleh klien HTTP; bukan praktik standar
  • Header HTTP Kustom - Mewajibkan header khusus umumnya bertentangan dengan praktik standar ; menggunakannya tidak konsisten dengan API saya lainnya, tidak ada yang memerlukan tajuk khusus; selanjutnya, tidak ada respons HTTP yang baik yang tersedia untuk menunjukkan nilai header khusus yang buruk (mungkin pertanyaan yang sama sekali berbeda)
  • Header HTTP Standar - Tidak ada header standar yang sesuai
  • Parameter Kueri - Menambahkan parameter kueri sebenarnya mengubah URI Permintaan yang sedang dihapus; bertentangan dengan praktik standar
  • Metode POST - (mis. POST /resourceToDelete { deletemetadata }) POST bukanlah opsi semantik untuk dihapus; POST sebenarnya mewakili tindakan berlawanan yang diinginkan (yaitu POST membuat bawahan sumber daya; tetapi saya perlu menghapus sumber daya)
  • Beberapa Metode - Memisahkan permintaan DELETE menjadi dua operasi (misalnya PUT menghapus metadata, lalu DELETE) membagi operasi atom menjadi dua, berpotensi meninggalkan status yang tidak konsisten. Alasan penghapusan (dan metadata terkait lainnya) bukan bagian dari representasi resource itu sendiri.

Preferensi pertama saya mungkin menggunakan badan pesan, kedua setelah header HTTP kustom; namun, seperti yang ditunjukkan, ada beberapa kelemahan dari pendekatan ini.

Apakah ada rekomendasi atau praktik terbaik yang sejalan dengan standar REST / HTTP untuk menyertakan metadata yang diperlukan tersebut pada permintaan DELETE? Apakah ada alternatif lain yang belum saya pertimbangkan?

shelley
sumber
2
Implementasi tertentu seperti Jerseytidak mengizinkan isi untuk deletepermintaan.
basiljames

Jawaban:

46

Terlepas dari beberapa rekomendasi untuk tidak menggunakan badan pesan untuk permintaan DELETE, pendekatan ini mungkin sesuai dalam kasus penggunaan tertentu. Ini adalah pendekatan yang akhirnya kami gunakan setelah mengevaluasi opsi lain yang disebutkan dalam pertanyaan / jawaban, dan setelah berkolaborasi dengan konsumen layanan.

Meskipun penggunaan badan pesan tidak ideal, tidak ada opsi lain yang juga cocok. Badan permintaan DELETE memungkinkan kami dengan mudah dan jelas menambahkan semantik di sekitar data / metadata tambahan yang diperlukan untuk menyertai operasi DELETE.

Saya masih terbuka untuk pemikiran dan diskusi lain, tetapi ingin menutup lingkaran tentang pertanyaan ini. Saya menghargai pemikiran dan diskusi semua orang tentang topik ini!

shelley
sumber
12
Ini ide yang buruk. Satu tempat di mana ini akan membuat Anda mendapat masalah adalah jika Anda nantinya memutuskan untuk menggunakan layanan akselerasi HTTP seperti Akamai EdgeConnect. Saya tahu pasti bahwa badan strip EdgeConnect dari permintaan HTTP DELETE (karena mereka mengkonsumsi bandwidth kemungkinan besar tidak valid). Kemungkinan juga layanan serupa melakukan hal yang sama (lihat fitur akselerasi Kindle, dan layanan mirip CDN lainnya). Anda mungkin harus mendesain ulang agar tidak menggunakan kata kerja HTTP untuk layanan Anda. Sebagian besar API tidak masuk akal jika menggunakan HTTP-verbs / classical-REST dan masalah transportasi kata kerja HTTP sangat sulit dipecahkan.
Gabe
3
Saya setuju dengan @Gabe, mengirim tubuh dengan metode yang tidak memiliki tubuh menurut definisi adalah cara pasti untuk kehilangan data secara acak saat bit Anda melintasi pipa internet, dan Anda akan kesulitan men-debugnya .
Nicholas Shanks
4
Komentar yang menentang penggunaan DELETE ini tidak relevan sampai mereka mengatasi masalah yang sangat valid yang dimiliki OP. Saya mencoba yang terbaik untuk mematuhi semangat REST, tetapi menghapus tanpa konkurensi yang optimis dan penghapusan yang tidak memiliki operasi batch atom tidak praktis dalam situasi kehidupan nyata. Ini adalah defisiensi serius dengan pola REST.
Quarkly
1
Saya dengan @Quarkly dalam hal ini. Saya tidak mengerti apa ide RESTFUL tentang bagaimana kita harus memeriksa konkurensi, dll. Pemeriksaan konkurensi tidak termasuk dalam klien.
Dirk Wessels
13

Apa yang tampaknya Anda inginkan adalah salah satu dari dua hal, tidak ada yang murni DELETE:

  1. Anda memiliki dua operasi, salah PUTsatu alasan penghapusan diikuti oleh DELETEsumber daya. Setelah dihapus, konten sumber daya tidak dapat lagi diakses oleh siapa pun. 'Alasan' tidak boleh berisi hyperlink ke sumber daya yang dihapus. Atau,
  2. Anda mencoba mengubah sumber daya dari state=activemenjadi state=deleteddengan menggunakan DELETEmetode ini. Sumber daya dengan status = dihapus diabaikan oleh API utama Anda tetapi mungkin masih dapat dibaca oleh admin atau seseorang dengan akses database. Ini diizinkan - DELETEtidak harus menghapus data pendukung untuk sumber daya, hanya untuk menghapus sumber daya yang diekspos di URI tersebut.

Operasi apa pun yang memerlukan badan pesan pada DELETEpermintaan dapat dipecah menjadi paling umum, POSTuntuk melakukan semua tugas yang diperlukan dengan badan pesan, dan a DELETE. Saya tidak melihat alasan untuk merusak semantik HTTP.

Nicholas Shanks
sumber
3
Apa yang terjadi jika PUTalasan berhasil dan DELETEsumber daya gagal? Bagaimana keadaan yang tidak konsisten dapat dicegah?
Lightman
1
@Lightman PUT hanya menetapkan maksud. Itu bisa ada tanpa DELETE yang sesuai, yang akan menunjukkan bahwa seseorang ingin menghapus tetapi gagal atau mereka berubah pikiran. Membalik urutan panggilan juga akan memungkinkan DELETE terjadi tanpa alasan - penyediaan alasan akan dianggap hanya sebagai anotasi. Karena kedua alasan inilah saya akan merekomendasikan penggunaan opsi 2 dari atas, yaitu mengubah status catatan yang mendasari seperti menyebabkan sumber HTTP menghilang dari URL saat ini. Seorang pengumpul sampah / admin kemudian dapat membersihkan catatan
Nicholas Shanks
7

Mengingat situasi yang Anda alami, saya akan mengambil salah satu pendekatan berikut:

  • Kirim PUT atau PATCH : Saya menyimpulkan bahwa operasi penghapusan bersifat virtual, yang pada dasarnya memerlukan alasan penghapusan. Oleh karena itu, saya yakin memperbarui rekaman melalui operasi PUT / PATCH adalah pendekatan yang valid, meskipun ini bukan operasi DELETE per se.
  • Gunakan parameter kueri : Sumber daya uri tidak sedang diubah. Saya sebenarnya berpikir ini juga pendekatan yang valid. Pertanyaan yang Anda tautkan berbicara tentang tidak mengizinkan penghapusan jika parameter kueri tidak ada. Dalam kasus Anda, saya hanya akan memiliki alasan default jika alasannya tidak ditentukan dalam string kueri. Sumber daya akan tetap ada resource/:id. Anda dapat membuatnya dapat ditemukan dengan tajuk Tautan pada sumber daya untuk setiap alasan (dengan reltag pada setiap alasan untuk mengidentifikasi alasan).
  • Gunakan titik akhir terpisah untuk setiap alasan : Menggunakan url seperti resource/:id/canceled. Ini benar-benar mengubah Request-URI dan jelas bukan RESTful. Sekali lagi, tajuk tautan dapat membuatnya dapat ditemukan.

Ingatlah bahwa REST bukanlah hukum atau dogma. Anggap saja lebih sebagai panduan. Jadi, jika masuk akal untuk tidak mengikuti panduan untuk domain masalah Anda, jangan. Pastikan saja konsumen API Anda diberi tahu tentang variansnya.

codeprogressi
sumber
Mengenai penggunaan parameter kueri, pemahaman saya adalah bahwa kueri adalah bagian dari Request-URI per bagian 3.2 , dan oleh karena itu menggunakan pendekatan ini (atau juga, titik akhir terpisah) bertentangan dengan definisi metode DELETE , seperti "sumber daya diidentifikasi oleh Request-URI "dihapus.
shelley
Sumber daya diidentifikasi oleh jalur uri. Jadi GET to /orders/:idakan mengembalikan resource yang sama seperti /orders/:id?exclude=orderdetails. String kueri hanya memberikan petunjuk ke server - dalam hal ini untuk mengecualikan detail pesanan dalam respons (jika didukung). Demikian pula, jika Anda mengirim DELETE ke /orders/:idatau /orders/:id?reason=canceledatau /orders/:id?reason=bad_credit, Anda masih bertindak pada sumber daya dasar yang sama. Untuk menjaga 'antarmuka seragam', saya akan memiliki alasan default sehingga mengirim parameter kueri tidak diperlukan.
codeprogressi
@shelley Anda benar dalam kekhawatiran Anda tentang string kueri. String kueri adalah bagian dari URI. Mengirim permintaan DELETE ke /foo?123berarti Anda menghapus sumber daya yang berbeda dari jika Anda mengirim DELETE ke /foo?456.
Nicholas Shanks
@codeprogressi Maaf, tapi banyak yang salah. Sumber daya diidentifikasi oleh seluruh URI, bukan hanya jalurnya. String kueri yang berbeda adalah sumber daya yang berbeda (dalam pengertian HTTP dari kata 'resource'). Selain itu, alasan default tidak diperlukan untuk antarmuka yang seragam. Istilah itu mengacu pada penggunaan GET, PUT, POST, PATCH dan DELETE dengan cara HTTP mendefinisikannya. Kesamaannya adalah antara vendor (vendor agen pengguna, vendor API, vendor proxy caching, ISP, dll.) Dan tidak dalam API sendiri (meskipun itu juga harus seragam dalam desain untuk kewarasan penggunanya!).
Nicholas Shanks
@Nicholas Saya tidak mengerti kebutuhan Anda untuk memperdebatkan diskusi yang berakhir tiga tahun lalu. Jawaban dan komentar yang saya buat valid dan benar dari sudut pandang REST-centric. REST bukan HTTP (atau implementasi vendor dari HTTP). Dalam konteks REST, resource-nya sama. Dan seperti yang saya katakan dalam jawaban saya, REST bukanlah hukum atau dogma, tetapi pedoman.
codeprogress
0

Saya sarankan Anda memasukkan metadata yang diperlukan sebagai bagian dari hierarki URI itu sendiri. Contoh (Naif):

Jika Anda perlu menghapus entri berdasarkan rentang tanggal, alih-alih meneruskan tanggal mulai dan tanggal akhir dalam isi atau sebagai parameter kueri, susun URI sedemikian rupa sehingga Anda meneruskan informasi yang diperlukan sebagai bagian dari URI.

misalnya

DELETE /entries/range/01012012/31122012 - Hapus semua entri antara 1 Januari 2012 hingga 31 Desember 2012

Semoga ini membantu.

Suresh Kumar
sumber
5
Tidak mencakup kasus-kasus seperti mengirimkan alasan untuk penghapusan yaitu kolom komentar.
Kugel
3
Wow. itu ide yang buruk. Jika Anda memiliki terlalu banyak meta data, pembatasan ukuran pada URI akan membengkak.
Balaji Boggaram Ramanarayan
1
Pendekatan ini tidak mengikuti praktik RESTful dan tidak disarankan karena Anda akan memiliki struktur URI yang berbelit-belit. Endpoint menjadi kacau dengan identifikasi sumber daya vs meta data yang saling terkait dan pada waktunya akan menjadi mimpi buruk pemeliharaan saat API Anda berubah. Jauh lebih disukai untuk memiliki yang rangeditentukan dalam parameter kueri atau muatan yang merupakan inti dari pertanyaan ini: untuk memahami pendekatan praktik terbaik untuk masalah yang akan saya katakan bukan ini.
digitaldreamer
@digitaldreamer - Saya tidak mengerti apa yang Anda maksud dengan struktur URI yang berbelit-belit? Juga, ini adalah HTTP DELETE jadi payload bukanlah pilihan tetapi parameter kueri ya.
Suresh Kumar