NOT : REST hakkında ilk kez okumak için zaman harcadığımda, idempotence doğru olmaya çalışmak için kafa karıştırıcı bir kavramdı. Daha fazla yorumun (ve Jason Hoetger'in cevabının ) gösterdiği gibi, orijinal cevabımda hala tam olarak doğru anlamadım . Bir süredir, Jason'ı etkili bir şekilde intihal etmekten kaçınmak için bu cevabı kapsamlı bir şekilde güncellemeye direndim, ancak şimdi düzenliyorum, çünkü, (yorumlarda) istendi.
Cevabımı okuduktan sonra, Jason Hoetger'in bu soruya mükemmel cevabını da okumanızı öneririm ve Jason'dan çalmadan cevabımı daha iyi hale getirmeye çalışacağım.
PUT neden idempotenttir?
RFC 2616 alıntıda belirttiğiniz gibi, PUT idempotent olarak kabul edilir. Bir kaynağı koyduğunuzda, bu iki varsayım yürürlüktedir:
Bir koleksiyona değil bir objeye başvuruyorsunuz.
Sağladığınız varlık tamamlandı ( tüm varlık).
Örneklerden birine bakalım.
{ "username": "skwee357", "email": "skwee357@domain.com" }
Bu belgeyi /users
önerdiğiniz gibi POST yaparsanız , aşağıdaki gibi bir varlığı geri alabilirsiniz:
## /users/1
{
"username": "skwee357",
"email": "skwee357@domain.com"
}
Bu varlığı daha sonra değiştirmek isterseniz PUT ve PATCH arasında seçim yaparsınız. Bir PUT şöyle görünebilir:
PUT /users/1
{
"username": "skwee357",
"email": "skwee357@gmail.com" // new email address
}
Aynı işlemi PATCH kullanarak da yapabilirsiniz. Bu şöyle görünebilir:
PATCH /users/1
{
"email": "skwee357@gmail.com" // new email address
}
Bu ikisi arasında hemen bir fark göreceksiniz. PUT, bu kullanıcıdaki tüm parametreleri içeriyordu, ancak PATCH yalnızca değiştirileni ( email
) içeriyordu .
PUT kullanırken, tüm varlığı gönderdiğiniz ve tam varlığın o URI'de bulunan herhangi bir varlığın yerini aldığı varsayılır . Yukarıdaki örnekte, PUT ve PATCH aynı hedefe ulaşmıştır: her ikisi de bu kullanıcının e-posta adresini değiştirir. Ancak PUT bunu tüm varlığı değiştirerek ele alırken PATCH yalnızca sağlanan alanları güncelleyerek diğerlerini yalnız bırakır.
PUT istekleri tüm varlığı içerdiğinden, aynı isteği tekrar tekrar verirseniz, her zaman aynı sonuca sahip olmalıdır (gönderdiğiniz veriler artık varlığın tüm verileridir). Bu nedenle PUT idempotenttir.
PUT kullanımı yanlış
Yukarıdaki PATCH verilerini bir PUT isteğinde kullanırsanız ne olur?
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!
}
(Bu sorunun amaçları için, sunucunun belirli bir zorunlu alana sahip olmadığını ve bunun olmasına izin vereceğini varsayıyorum ... gerçekte durum böyle olmayabilir.)
PUT kullandığımız için, ancak sadece tedarik ettiğimizden email
, şimdi bu varlıktaki tek şey bu. Bu veri kaybına neden oldu.
Bu örnek açıklama amaçlıdır - bunu asla yapma. Bu PUT isteği teknik olarak idempotenttir, ancak bu korkunç, kırık bir fikir olmadığı anlamına gelmez.
PATCH nasıl idempotent olabilir?
Yukarıdaki örnekte, yama olarak İdempotent. Bir değişiklik yaptınız, ancak aynı değişikliği tekrar tekrar yaptıysanız, her zaman aynı sonucu verir: e-posta adresini yeni değere değiştirdiniz.
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
}
Orijinal örneğim, doğruluk için düzeltildi
Başlangıçta, idempotency olmadığını düşündüğüm örnekler vardı, ama yanıltıcı / yanlış. Örnekleri saklayacağım, ancak bunları farklı bir şeyi göstermek için kullanacağım: aynı varlığa karşı birden fazla PATCH belgesi, farklı nitelikleri değiştirerek, PATCH'leri idempotent yapmaz.
Diyelim ki geçmiş bir zamanda bir kullanıcı eklendi. Bu sizin başladığınız durumdur.
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@olddomain.com",
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "10001"
}
Bir PATCH sonrasında, değiştirilmiş bir varlığınız var:
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"
}
Daha sonra PATCH'inizi tekrar tekrar uygularsanız, aynı sonucu almaya devam edersiniz: e-posta yeni değere değiştirildi. A giriyor, A çıkıyor, bu yüzden bu idempotent.
Bir saat sonra, biraz kahve yapmaya ve ara vermeye gittikten sonra, başka biri kendi YAMA ile birlikte gelir. Postane bazı değişiklikler yapıyor gibi görünüyor.
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
}
Postaneden gelen bu PATCH, e-posta ile ilgilenmediğinden, yalnızca posta kodu, tekrar tekrar uygulanırsa, aynı sonucu alır: posta kodu yeni değere ayarlanır. A girer, A çıkar, bu yüzden bu da idempotenttir.
Ertesi gün, PATCH'inizi tekrar göndermeye karar veriyorsunuz.
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"
}
Düzeltme ekiniz dünle aynı etkiye sahip: e-posta adresini ayarladı. A girdi, A çıktı, bu yüzden bu da idempotent.
Orijinal cevabımda neyi yanlış yaptım
Önemli bir ayrım yapmak istiyorum (orijinal cevabımda yanlış yaptığım bir şey). Birçok sunucu, değişikliklerinizle (varsa) yeni varlık durumunu geri göndererek REST isteklerinize yanıt verir. Yani, bu yanıtı geri aldığınızda, dün aldığınız yanıttan farklıdır , çünkü posta kodu son kez aldığınız yanıt değildir. Ancak, isteğiniz posta kodu ile değil, yalnızca e-posta ile ilgiliydi. Dolayısıyla, PATCH belgeniz hala idempotent - PATCH'de gönderdiğiniz e-posta artık varlığın e-posta adresidir.
Peki PATCH ne zaman idempotent değil?
Bu sorunun tam bir tedavisi için sizi tekrar Jason Hoetger'in cevabına yönlendiriyorum . Sadece bu konuda bırakacağım, çünkü dürüstçe bu kısma zaten sahip olduğundan daha iyi cevap verebileceğimi sanmıyorum.