Pourquoi la méthode PATCH n'est pas idempotente?

48

Je me demandais à ce sujet.

Supposons que j'ai une userressource avec idet des namechamps. Si je veux mettre à jour un champ, je pourrais simplement faire une requête PATCH à la ressource comme ceci

PATCH /users/42
{"name": "john doe"} 

Et puis l'application mettra à jour le nom d'utilisateur 42.

Mais pourquoi si je répète cette demande, le résultat serait différent?

Selon RFC 5789

PATCH n'est ni sûr ni idempotent

Mattecapu
la source
Et si entre les appels, quelqu'un d'autre fait une demande de mise à jour de l'utilisateur 42{"name": "bendjamin franklin"}
gnat
@gnat Un argument similaire ne s'applique-t-il pas également à la méthode PUT, qui est plutôt considérée comme idempotente? (voir goo.gl/t24ZSJ )
Matécapu
"PUT a une sémantique idempotente et peut donc être utilisé en toute sécurité pour les mises à jour absolues (c'est-à-dire que nous envoyons l'état complet de la ressource au serveur), mais pas aussi pour les mises à jour relatives (c'est-à-dire que nous n'envoyons que des modifications de l'état de la ressource) , car cela violerait sa sémantique ... "( Requêtes POST et PUT - est-ce juste la convention? )
mord
1
Évidemment ... Mais vous pourriez dire que PUT n'est pas idempotent, car entre deux demandes égales, un deuxième client pourrait faire une troisième demande entre les deux. Mais comme nous ne nous soucions pas des données précédentes, ce n'est pas un problème. Le même argument est valable pour les requêtes PATCH.
mattecapu
2
J'ai pris la liberté d'ajouter une référence à la spécification pertinente, car je pense que cela est très pertinent dans le contexte de cette question.
Pete

Réponses:

34

Une requête PATCH peut être idempotente, mais ce n’est pas obligatoire. C'est la raison pour laquelle il est qualifié de non-idempotent.

Que PATCH soit idempotent ou non dépend fortement de la manière dont les modifications requises sont communiquées.
Par exemple, si le format du correctif est sous la forme {change: 'Name' from: 'benjamin franklin' to: 'john doe'}, alors toute demande PATCH après la première aurait un effet différent (une réponse en échec) de la première demande.
Une autre raison de non-idempotence peut être que l'application de la modification sur autre chose que la ressource d'origine peut rendre la ressource invalide. Ce serait également le cas si vous appliquez la modification plusieurs fois.

Bart van Ingen Schenau
la source
3
Je ne comprends pas le dernier paragraphe. Pouvez-vous donner un exemple illustrant comment "appliquer la modification à une ressource autre que la ressource d'origine peut rendre la ressource invalide", et en quoi cela est lié à l'application répétée d'une modification à la même ressource?
Robin Green
3
@RobinGreen: supposons que la ressource soit représentée en XML avec la condition qu'il y ait au plus un <name>élément. Si le PATCH ajoute un <name>élément à une ressource qui n'en contenait pas à l'origine, appliquer le PATCH deux fois (ou l'appliquer à une ressource qui contient déjà a <name>) rend la ressource invalide, car elle contiendrait tout à coup deux <name>éléments non autorisés. pour de telles ressources.
Bart van Ingen Schenau
13
Le premier exemple que vous avez donné ne concerne pas IMHO, car il est idempotent. Exemple avec DELETE qui est idempotent, la première requête: la ressource existait mais a été supprimée (renvoie 2xx), la deuxième requête: la ressource n'existe pas et n'existe toujours pas après la demande, renvoie 4xx. L'état du serveur n'a pas changé, donc l'idempotency. Dans votre exemple, première demande: PATCH de BenFra à JohDoe, le nom de la ressource était BenFra, mais JohDoe (renvoie 2xx). Donc, cela ne montre pas que PATCH peut être non-impotent.
Sp00m
Plus de détails ici: stackoverflow.com/q/4088350/1225328
sp00m
8

Je pense que la réponse claire lorsque PATCH n'est pas idempotente est ce paragraphe de la RFC 5789:

Il existe également des cas où les formats de correctifs n'ont pas besoin d'opérer à partir d'un point de base connu (par exemple, l'ajout de lignes de texte aux fichiers journaux ou de lignes non en conflit dans les tables de base de données), auquel cas le même soin n'est pas nécessaire dans les demandes des clients. .

Comme RFC spécifie que le correctif contient des "modifications générales" à la ressource, nous devrions regarder au-delà du simple remplacement de champ. Si la ressource est pour un compteur, alors patch peut demander son incrément, ce qui n'est clairement pas idempotet.

Ivan
la source
2

PATCHLes requêtes décrivent un ensemble d'opérations à appliquer à une ressource. Si vous appliquez deux fois le même ensemble d'opérations à la même ressource, le résultat risque de ne pas être identique. C'est parce que vous définissez les opérations. En d'autres termes, vous devez définir les règles de fusion .

N'oubliez pas qu'une PATCHdemande peut être utilisée pour patcher des ressources dans de nombreux formats différents, pas seulement JSON.

Ainsi, une PATCHdemande peut être idempotente si vous définissez les règles de fusion comme étant idempotentes .

Exemple idempotent:

// Original resource
{
  name: 'Tito',
  age: 32
}

// PATCH request
{
  age: 33
}

// New resource
{
  name: 'Tito',
  age: 33
}

Exemple non idempotent:

// Original resource
{
  name: 'Tito',
  age: 32
}

// PATCH request
{
  $increment: 'age'
}

// New resource
{
  name: 'Tito',
  age: 33
}

Dans le deuxième exemple, j'ai utilisé une syntaxe "comme Mongo" que j'ai créée pour incrémenter un attribut. Clairement, cela n’est pas idempotent, car l’envoi de la même demande plusieurs fois donnerait des résultats différents à chaque fois.

Maintenant, vous vous demandez peut-être si l’utilisation d’une telle syntaxe est valide. Selon les standards , c'est:

La différence entre les requêtes PUT et PATCH est reflétée dans la façon dont le serveur traite l'entité incluse pour modifier la ressource identifiée par l'URI de demande. Dans une demande PUT, l'entité incluse est considérée comme une version modifiée de la ressource stockée sur le serveur d'origine et le client demande le remplacement de la version stockée. Cependant, avec PATCH, l'entité incluse contient un ensemble d'instructions décrivant la manière dont une ressource résidant actuellement sur le serveur d'origine doit être modifiée pour produire une nouvelle version.

Et vous vous demandez peut-être s'il est reposant d'utiliser les PATCHdemandes de cette façon, et de nombreuses personnes considèrent que non, voici une bonne réponse avec de nombreux commentaires sur le sujet.

Jbm
la source