CATATAN : Ketika saya pertama kali menghabiskan waktu membaca tentang REST, idempoten adalah konsep yang membingungkan untuk dicoba. Saya masih belum mengerti jawaban asli saya, karena komentar lebih lanjut (dan ditunjukkan oleh jawaban Jason Hoetger ). Untuk sementara, saya telah menolak memperbarui jawaban ini secara ekstensif, untuk menghindari menjiplak Jason secara efektif, tetapi saya mengeditnya sekarang karena, yah, saya diminta (dalam komentar).
Setelah membaca jawaban saya, saya sarankan Anda juga membaca jawaban Jason Hoetger yang luar biasa untuk pertanyaan ini, dan saya akan mencoba membuat jawaban saya lebih baik tanpa hanya mencuri dari Jason.
Mengapa PUT idempoten?
Seperti yang Anda catat dalam kutipan RFC 2616 Anda, PUT dianggap idempoten. Ketika Anda PUT sumber daya, dua asumsi ini dalam permainan:
Anda merujuk pada suatu entitas, bukan ke koleksi.
Entitas yang Anda berikan sudah lengkap ( seluruh entitas).
Mari kita lihat salah satu contoh Anda.
{ "username": "skwee357", "email": "skwee357@domain.com" }
Jika Anda POST dokumen ini /users
, seperti yang Anda sarankan, maka Anda mungkin mendapatkan kembali entitas seperti
## /users/1
{
"username": "skwee357",
"email": "skwee357@domain.com"
}
Jika Anda ingin memodifikasi entitas ini nanti, Anda memilih antara PUT dan PATCH. PUT mungkin terlihat seperti ini:
PUT /users/1
{
"username": "skwee357",
"email": "skwee357@gmail.com" // new email address
}
Anda dapat melakukan hal yang sama menggunakan PATCH. Itu mungkin terlihat seperti ini:
PATCH /users/1
{
"email": "skwee357@gmail.com" // new email address
}
Anda akan segera melihat perbedaan antara keduanya. PUT menyertakan semua parameter pada pengguna ini, tetapi PATCH hanya menyertakan parameter yang sedang dimodifikasi ( email
).
Saat menggunakan PUT, diasumsikan bahwa Anda mengirim entitas yang lengkap, dan entitas yang lengkap itu menggantikan entitas yang ada di URI itu. Pada contoh di atas, PUT dan PATCH mencapai tujuan yang sama: keduanya mengubah alamat email pengguna ini. Tapi PUT menanganinya dengan mengganti seluruh entitas, sementara PATCH hanya memperbarui bidang yang disediakan, meninggalkan yang lain sendirian.
Karena permintaan PUT menyertakan seluruh entitas, jika Anda mengeluarkan permintaan yang sama berulang kali, itu harus selalu memiliki hasil yang sama (data yang Anda kirim sekarang adalah seluruh data entitas). Karena itu PUT idempoten.
Menggunakan PUT salah
Apa yang terjadi jika Anda menggunakan data PATCH di atas dalam permintaan PUT?
GET /users/1
{
"username": "skwee357",
"email": "skwee357@domain.com"
}
PUT /users/1
{
"email": "skwee357@gmail.com" // new email address
}
GET /users/1
{
"email": "skwee357@gmail.com" // new email address... and nothing else!
}
(Saya berasumsi untuk keperluan pertanyaan ini bahwa server tidak memiliki bidang khusus yang diperlukan, dan akan memungkinkan ini terjadi ... yang mungkin tidak terjadi pada kenyataannya.)
Karena kami menggunakan PUT, tetapi hanya memasok email
, sekarang itulah satu-satunya hal dalam entitas ini. Ini telah menyebabkan hilangnya data.
Contoh ini ada di sini untuk tujuan ilustrasi - jangan pernah benar-benar melakukan ini. Permintaan PUT ini secara teknis idempoten, tetapi itu tidak berarti itu bukan ide yang buruk dan rusak.
Bagaimana PATCH bisa idempoten?
Dalam contoh di atas, PATCH adalah idempoten. Anda membuat perubahan, tetapi jika Anda membuat perubahan yang sama berulang kali, itu akan selalu memberikan hasil yang sama: Anda mengubah alamat email ke nilai yang baru.
GET /users/1
{
"username": "skwee357",
"email": "skwee357@domain.com"
}
PATCH /users/1
{
"email": "skwee357@gmail.com" // new email address
}
GET /users/1
{
"username": "skwee357",
"email": "skwee357@gmail.com" // email address was changed
}
PATCH /users/1
{
"email": "skwee357@gmail.com" // new email address... again
}
GET /users/1
{
"username": "skwee357",
"email": "skwee357@gmail.com" // nothing changed since last GET
}
Contoh asli saya, diperbaiki untuk akurasi
Saya awalnya punya contoh yang saya pikir menunjukkan non-idempotency, tetapi mereka menyesatkan / salah. Saya akan menyimpan contoh-contohnya, tetapi menggunakannya untuk mengilustrasikan hal yang berbeda: bahwa banyak dokumen PATCH terhadap entitas yang sama, memodifikasi atribut yang berbeda, tidak membuat PATCH non-idempoten.
Katakanlah pada waktu yang lalu, pengguna telah ditambahkan. Ini adalah negara tempat Anda memulai.
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@olddomain.com",
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "10001"
}
Setelah PATCH, Anda memiliki entitas yang dimodifikasi:
PATCH /users/1
{"email": "skwee357@newdomain.com"}
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@newdomain.com", // the email changed, yay!
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "10001"
}
Jika Anda berulang kali menerapkan PATCH, Anda akan terus mendapatkan hasil yang sama: email diubah ke nilai baru. A masuk, A keluar, oleh karena itu idempoten.
Satu jam kemudian, setelah Anda membuat kopi dan beristirahat, orang lain datang dengan PATCH mereka sendiri. Tampaknya Kantor Pos telah melakukan beberapa perubahan.
PATCH /users/1
{"zip": "12345"}
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@newdomain.com", // still the new email you set
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "12345" // and this change as well
}
Karena PATCH dari kantor pos ini tidak berkaitan dengan email, hanya kode pos, jika diterapkan berulang kali, itu juga akan mendapatkan hasil yang sama: kode pos disetel ke nilai baru. A masuk, A keluar, oleh karena itu ini juga idempoten.
Hari berikutnya, Anda memutuskan untuk mengirim PATCH lagi.
PATCH /users/1
{"email": "skwee357@newdomain.com"}
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@newdomain.com",
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "12345"
}
Tambalan Anda memiliki efek yang sama dengan kemarin: ia menetapkan alamat email. A masuk, A keluar, oleh karena itu ini idempoten juga.
Apa yang salah dalam jawaban awal saya
Saya ingin menggambar perbedaan penting (sesuatu yang saya salah dalam jawaban asli saya). Banyak server akan menanggapi permintaan REST Anda dengan mengirim kembali status entitas baru, dengan modifikasi Anda (jika ada). Jadi, ketika Anda mendapatkan respons ini kembali, itu berbeda dari yang Anda dapatkan kemarin , karena kode pos bukan yang Anda terima terakhir kali. Namun, permintaan Anda tidak terkait dengan kode pos, hanya dengan email. Jadi dokumen PATCH Anda masih idempoten - email yang Anda kirim di PATCH sekarang adalah alamat email pada entitas.
Jadi kapan PATCH tidak idempoten?
Untuk perawatan lengkap dari pertanyaan ini, saya kembali merujuk Anda ke jawaban Jason Hoetger . Saya hanya akan berhenti di situ, karena saya jujur tidak berpikir saya bisa menjawab bagian ini lebih baik daripada yang sudah dia miliki.