Misalkan REST API, sebagai tanggapan atas GET
permintaan HTTP , mengembalikan beberapa data tambahan dalam sub-objek owner
:
{
id: 'xyz',
... some other data ...
owner: {
name: 'Jo Bloggs',
role: 'Programmer'
}
}
Jelas, kami tidak ingin siapa pun dapat PUT
kembali
{
id: 'xyz',
... some other data ...
owner: {
name: 'Jo Bloggs',
role: 'CEO'
}
}
dan telah berhasil itu. Memang, kami mungkin tidak akan menerapkan cara untuk itu bahkan berpotensi berhasil, dalam hal ini.
Tetapi pertanyaan ini bukan hanya tentang sub-objek: apa, secara umum, yang harus dilakukan dengan data yang tidak boleh dimodifikasi dalam permintaan PUT?
Haruskah dihilangkan dari permintaan PUT?
Haruskah itu dibuang secara diam-diam?
Haruskah dicentang, dan jika berbeda dari nilai atribut yang lama, kembalikan kode kesalahan HTTP dalam respons?
Atau haruskah kita menggunakan patch JSON RFC 6902 alih-alih mengirim seluruh JSON?
rest
http
http-request
Robin Green
sumber
sumber
Jawaban:
Tidak ada aturan, baik dalam spesifikasi W3C atau aturan tidak resmi REST, yang mengatakan bahwa a
PUT
harus menggunakan skema / model yang sama dengan yang sesuaiGET
.Sangat menyenangkan jika mereka serupa , tetapi itu tidak biasa untuk
PUT
melakukan hal-hal yang sedikit berbeda. Misalnya, saya telah melihat banyak API yang menyertakan beberapa jenis ID dalam konten yang dikembalikan olehGET
, untuk kenyamanan. Tetapi dengan aPUT
, ID itu ditentukan secara eksklusif oleh URI dan tidak memiliki makna dalam konten. ID apa pun yang ditemukan dalam tubuh akan diabaikan secara diam-diam.REST dan web pada umumnya sangat terikat dengan Prinsip Robustness : "Jadilah konservatif dalam apa yang Anda kirim, menjadi liberal dalam apa yang Anda terima." Jika Anda setuju secara filosofis dengan ini, maka solusinya jelas: Abaikan data yang tidak valid dalam
PUT
permintaan. Itu berlaku untuk data yang tidak dapat diubah, seperti pada contoh Anda, dan omong kosong yang sebenarnya, misalnya bidang yang tidak dikenal.PATCH
berpotensi pilihan lain, tetapi Anda tidak boleh menerapkanPATCH
kecuali Anda benar-benar akan mendukung pembaruan parsial.PATCH
berarti hanya memperbarui atribut spesifik yang saya sertakan dalam konten ; itu tidak berarti mengganti seluruh entitas tetapi mengecualikan beberapa bidang tertentu . Apa yang sebenarnya Anda bicarakan sebenarnya bukan pembaruan parsial, ini adalah pembaruan penuh, idempoten dan semuanya, hanya saja sebagian dari sumber daya tersebut hanya baca-saja.Suatu hal yang menyenangkan untuk dilakukan jika Anda memilih opsi ini adalah mengirim kembali 200 (OK) dengan entitas yang sebenarnya diperbarui dalam respons, sehingga klien dapat melihat dengan jelas bahwa bidang baca-saja tidak diperbarui.
Pasti ada beberapa orang yang berpikir sebaliknya - bahwa itu seharusnya merupakan kesalahan untuk mencoba memperbarui bagian read-only dari sumber daya. Ada beberapa pembenaran untuk ini, terutama atas dasar bahwa Anda pasti akan mengembalikan kesalahan jika seluruh sumber daya hanya-baca dan pengguna mencoba memperbaruinya. Ini jelas bertentangan dengan prinsip ketahanan, tetapi Anda mungkin menganggapnya lebih "mendokumentasikan diri sendiri" untuk pengguna API Anda.
Ada dua konvensi untuk ini, keduanya sesuai dengan ide asli Anda, tetapi saya akan mengembangkannya. Yang pertama adalah untuk melarang bidang baca-saja muncul di konten, dan mengembalikan HTTP 400 (Permintaan Buruk) jika mereka melakukannya. API semacam ini juga harus mengembalikan HTTP 400 jika ada bidang lain yang tidak dikenal / tidak dapat digunakan. Yang kedua adalah mengharuskan bidang baca-saja identik dengan konten saat ini, dan mengembalikan 409 (Konflik) jika nilainya tidak cocok.
Saya sangat tidak suka dengan pemeriksaan kesetaraan dengan 409 karena selalu membutuhkan klien untuk melakukan
GET
untuk mengambil data saat ini sebelum dapat melakukanPUT
. Itu tidak baik dan mungkin akan menyebabkan kinerja yang buruk, untuk seseorang, di suatu tempat. Saya juga sangat tidak suka 403 (Terlarang) untuk ini karena ini menyiratkan bahwa seluruh sumber daya dilindungi, bukan hanya bagian dari itu. Jadi pendapat saya adalah, jika Anda benar-benar harus memvalidasi alih-alih mengikuti prinsip ketahanan, validasi semua permintaan Anda dan kembalikan 400 untuk apa pun yang memiliki bidang tambahan atau tidak dapat ditulis.Pastikan 400/409 / apa pun Anda termasuk informasi tentang apa masalah spesifik itu dan bagaimana cara memperbaikinya.
Kedua pendekatan ini valid, tetapi saya lebih suka yang pertama sesuai dengan prinsip ketahanan. Jika Anda pernah mengalami bekerja dengan API REST besar, Anda akan menghargai nilai kompatibilitas ke belakang. Jika Anda pernah memutuskan untuk menghapus bidang yang ada atau membuatnya hanya-baca, itu adalah perubahan yang kompatibel jika server mengabaikan bidang-bidang itu, dan klien lama masih akan bekerja. Namun, jika Anda melakukan validasi ketat pada konten, itu tidak kompatibel lagi, dan klien lama akan berhenti bekerja. Yang pertama umumnya berarti lebih sedikit pekerjaan untuk pengelola API dan kliennya.
sumber
Potensi idem
Setelah RFC, seorang PUT harus mengirimkan objek penuh ke sumber daya. Alasan utama ini, adalah bahwa PUT harus idempoten. Ini berarti permintaan, yang diulang harus mengevaluasi hasil yang sama di server.
Jika Anda mengizinkan pembaruan parsial, itu tidak bisa lagi menjadi potensi. Jika Anda memiliki dua klien. Klien A dan B, maka skenario berikut dapat berkembang:
Klien A mendapatkan gambar dari gambar sumber. Ini berisi deskripsi gambar, yang masih valid. Klien B menempatkan gambar baru dan memperbarui deskripsi yang sesuai. Gambar telah berubah. Klien A melihat, dia tidak perlu mengubah deskripsi, karena itu sesuai keinginannya dan hanya menempatkan gambar.
Ini akan menyebabkan inkonsistensi, gambar memiliki metadata yang salah terpasang!
Yang lebih menjengkelkan adalah bahwa perantara mana pun dapat mengulangi permintaan itu. Dalam kasus itu memutuskan entah bagaimana PUT gagal.
Arti PUT tidak dapat diubah (meskipun Anda dapat menyalahgunakannya).
Pilihan lain
Untungnya ada opsi lain, ini adalah PATCH. PATCH adalah metode yang memungkinkan Anda memperbarui sebagian struktur. Anda cukup mengirim struktur parsial. Untuk aplikasi sederhana, ini baik-baik saja. Metode ini tidak dijamin ampuh. Klien harus mengirim permintaan dalam bentuk berikut:
Dan server dapat membalas kembali dengan 204 (Tidak ada konten) untuk menandai keberhasilan. Kesalahan Anda tidak dapat memperbarui bagian dari struktur. Metode PATCH adalah atom.
Kelemahan dari metode ini adalah, tidak semua browser mendukung ini, tetapi ini adalah opsi paling alami dalam layanan REST.
Contoh tambalan permintaan: http://tools.ietf.org/html/rfc5789#section-2.1
Json menambal
Opsi json tampaknya cukup komprehensif dan opsi yang menarik. Tetapi bisa sulit untuk diterapkan untuk pihak ketiga. Anda harus memutuskan apakah basis pengguna Anda dapat menangani ini.
Ini juga agak berbelit-belit, karena Anda perlu membangun penerjemah kecil yang mengubah perintah menjadi struktur parsial, yang akan Anda gunakan untuk memperbarui model Anda. Penerjemah ini juga harus memeriksa, apakah perintah yang diberikan masuk akal. Beberapa perintah membatalkan satu sama lain. (tulis fielda, hapus fielda). Saya pikir Anda ingin melaporkan ini kembali ke klien untuk membatasi waktu debug di pihaknya.
Tetapi jika Anda punya waktu, ini adalah solusi yang sangat elegan. Anda masih harus memvalidasi bidang tentu saja. Anda dapat menggabungkan ini dengan metode PATCH untuk tetap menjadi model REST. Tapi saya pikir POST dapat diterima di sini.
Menjadi buruk
Jika Anda memutuskan untuk menggunakan opsi PUT, yang agak berisiko. Maka Anda setidaknya tidak harus membuang kesalahan. Pengguna memiliki harapan tertentu (data akan diperbarui) dan jika Anda memecahkan ini, Anda akan memberikan beberapa pengembang bukan waktu yang baik.
Anda dapat memilih untuk mundur: 409 Konflik atau 403 Dilarang. Tergantung bagaimana Anda melihat proses pembaruan. Jika Anda melihatnya sebagai seperangkat aturan (sistem-sentris), maka konflik akan lebih baik. Sesuatu seperti, bidang ini tidak dapat diperbarui. (Bertentangan dengan aturan). Jika Anda melihatnya sebagai masalah otorisasi (user-centric), maka Anda harus kembali terlarang. Dengan: Anda tidak diizinkan mengubah bidang ini.
Anda masih harus memaksa pengguna untuk mengirim semua bidang yang dapat dimodifikasi.
Pilihan yang masuk akal untuk menegakkan ini adalah untuk mengaturnya ke sub-sumber daya, yang hanya menawarkan data yang dapat dimodifikasi.
Opini pribadi
Secara pribadi saya akan pergi (jika Anda tidak harus bekerja dengan browser) untuk model PATCH sederhana dan kemudian memperpanjangnya dengan prosesor patch JSON. Ini dapat dilakukan dengan membedakan pada mimetypes: Tipe mime dari patch json:
aplikasi / json-patch
Dan json: application / json-patch
membuatnya mudah untuk mengimplementasikannya dalam dua fase.
sumber