NOTA : Cuando pasé tiempo leyendo sobre REST, la idempotencia era un concepto confuso para tratar de acertar. Todavía no lo entendí bien en mi respuesta original, como lo han demostrado otros comentarios (y la respuesta de Jason Hoetger ). Por un tiempo, me he resistido a actualizar esta respuesta ampliamente, para evitar plagiar efectivamente a Jason, pero lo estoy editando ahora porque, bueno, me lo pidieron (en los comentarios).
Después de leer mi respuesta, le sugiero que también lea la excelente respuesta de Jason Hoetger a esta pregunta, y trataré de mejorar mi respuesta sin simplemente robarle a Jason.
¿Por qué es PUT idempotente?
Como notó en su cita RFC 2616, PUT se considera idempotente. Cuando pones un recurso, estas dos suposiciones están en juego:
Se refiere a una entidad, no a una colección.
La entidad que está proporcionando está completa (la entidad completa ).
Veamos uno de tus ejemplos.
{ "username": "skwee357", "email": "skwee357@domain.com" }
Si envía este documento a /users
, como sugiere, entonces podría recuperar una entidad como
## /users/1
{
"username": "skwee357",
"email": "skwee357@domain.com"
}
Si desea modificar esta entidad más tarde, elija entre PUT y PATCH. Un PUT podría verse así:
PUT /users/1
{
"username": "skwee357",
"email": "skwee357@gmail.com" // new email address
}
Puede lograr lo mismo usando PATCH. Eso podría verse así:
PATCH /users/1
{
"email": "skwee357@gmail.com" // new email address
}
Notarás una diferencia de inmediato entre estos dos. El PUT incluyó todos los parámetros de este usuario, pero PATCH solo incluyó el que se estaba modificando ( email
).
Al usar PUT, se supone que está enviando la entidad completa, y esa entidad completa reemplaza a cualquier entidad existente en ese URI. En el ejemplo anterior, PUT y PATCH logran el mismo objetivo: ambos cambian la dirección de correo electrónico de este usuario. Pero PUT lo maneja reemplazando toda la entidad, mientras que PATCH solo actualiza los campos que se proporcionaron, dejando a los demás solos.
Dado que las solicitudes PUT incluyen a toda la entidad, si emite la misma solicitud repetidamente, siempre debe tener el mismo resultado (los datos que envió ahora son todos los datos de la entidad). Por lo tanto, PUT es idempotente.
Usando PUT incorrecto
¿Qué sucede si usa los datos PATCH anteriores en una solicitud 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!
}
(Asumo a los fines de esta pregunta que el servidor no tiene campos obligatorios específicos, y permitiría que esto suceda ... eso puede no ser el caso en realidad).
Como usamos PUT, pero solo lo suministramos email
, ahora eso es lo único en esta entidad. Esto ha resultado en pérdida de datos.
Este ejemplo está aquí con fines ilustrativos, nunca lo hagas. Esta solicitud PUT es técnicamente idempotente, pero eso no significa que no sea una idea terrible y rota.
¿Cómo puede PATCH ser idempotente?
En el ejemplo anterior, PATCH era idempotente. Realizó un cambio, pero si realizó el mismo cambio una y otra vez, siempre devolvería el mismo resultado: cambió la dirección de correo electrónico al nuevo valor.
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
}
Mi ejemplo original, corregido por precisión
Originalmente tenía ejemplos que pensé que mostraban no idempotencia, pero eran engañosos / incorrectos. Voy a mantener los ejemplos, pero los usaré para ilustrar algo diferente: que varios documentos de PATCH contra la misma entidad, modificando diferentes atributos, no hacen que los PATCH sean no idempotentes.
Digamos que en algún momento pasado, se agregó un usuario. Este es el estado desde el que estás comenzando.
{
"id": 1,
"name": "Sam Kwee",
"email": "skwee357@olddomain.com",
"address": "123 Mockingbird Lane",
"city": "New York",
"state": "NY",
"zip": "10001"
}
Después de un PARCHE, tiene una entidad modificada:
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"
}
Si luego aplica repetidamente su PATCH, continuará obteniendo el mismo resultado: el correo electrónico se cambió al nuevo valor. A entra, A sale, por lo tanto, esto es idempotente.
Una hora más tarde, después de que hayas ido a tomar un café y tomar un descanso, alguien más viene con su propio PARCHE. Parece que la oficina de correos ha estado haciendo algunos cambios.
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
}
Dado que este parche de la oficina de correos no se refiere al correo electrónico, solo al código postal, si se aplica repetidamente, también obtendrá el mismo resultado: el código postal se establece en el nuevo valor. A entra, A sale, por lo tanto, esto también es idempotente.
Al día siguiente, decides enviar tu PATCH nuevamente.
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"
}
Su parche tiene el mismo efecto que tuvo ayer: estableció la dirección de correo electrónico. A entró, salió A, por lo tanto, esto también es idempotente.
Lo que me equivoqué en mi respuesta original
Quiero hacer una distinción importante (algo que me equivoqué en mi respuesta original). Muchos servidores responderán a sus solicitudes REST enviando el nuevo estado de la entidad, con sus modificaciones (si corresponde). Entonces, cuando reciba esta respuesta , es diferente de la que recibió ayer , porque el código postal no es el que recibió la última vez. Sin embargo, su solicitud no estaba relacionada con el código postal, solo con el correo electrónico. Por lo tanto, su documento PATCH sigue siendo idempotente: el correo electrónico que envió en PATCH ahora es la dirección de correo electrónico de la entidad.
Entonces, ¿cuándo PATCH no es idempotente?
Para un tratamiento completo de esta pregunta, nuevamente lo remito a la respuesta de Jason Hoetger . Solo voy a dejarlo así, porque honestamente no creo que pueda responder esta parte mejor de lo que ya lo ha hecho.