Warum ist die PATCH-Methode nicht idempotent?

48

Ich habe mich darüber gewundert.

Angenommen, ich habe eine userRessource mit idund nameFeldern. Wenn ich ein Feld aktualisieren möchte, könnte ich einfach eine PATCH-Anfrage an die Ressource wie folgt senden

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

Anschließend aktualisiert die Anwendung den Namen des Benutzers 42.

Aber warum ist das Ergebnis anders, wenn ich diese Aufforderung wiederhole?

Gemäß RFC 5789

PATCH ist weder sicher noch idempotent

Mattecapu
quelle
was ist, wenn zwischen Anrufungen jemand anderes eine Anfrage an Update Benutzer 42{"name": "bendjamin franklin"}
gnat
@gnat gilt ein ähnliches Argument nicht auch für die PUT-Methode, die stattdessen als idempotent gilt? (siehe goo.gl/t24ZSJ )
Mattecapu
"PUT hat eine idempotente Semantik und kann daher sicher für absolute Aktualisierungen verwendet werden (dh wir senden den gesamten Status der Ressource an den Server), aber nicht auch für relative Aktualisierungen (dh wir senden nur Änderungen am Status der Ressource) , da dies verstoßen würde seine Semantik ... "( POST- und PUT-Anfragen - ist es nur die Konvention? )
gnat
1
Offensichtlich ... Aber Sie könnten sagen, dass PUT nicht idempotent ist, weil zwischen zwei gleichen Anforderungen ein zweiter Client eine dritte Anforderung zwischen den beiden stellen könnte. Da wir uns aber nicht um vorherige Daten kümmern, ist dies kein Problem. Das gleiche Argument gilt für PATCH-Anfragen.
Mattecapu
2
Ich habe mir erlaubt, einen Verweis auf die einschlägige Spezifikation hinzuzufügen, da dies meines Erachtens im Zusammenhang mit dieser Frage von großer Relevanz ist.
Pete

Antworten:

34

Eine PATCH-Anfrage kann idempotent sein, muss es aber nicht. Das ist der Grund, warum es als nicht idempotent charakterisiert wird.

Ob PATCH idempotent sein kann oder nicht, hängt stark davon ab, wie die erforderlichen Änderungen kommuniziert werden.
Wenn das Patch-Format beispielsweise die Form von hat {change: 'Name' from: 'benjamin franklin' to: 'john doe'}, hat jede PATCH-Anforderung nach der ersten eine andere Auswirkung (eine Fehlerantwort) als die erste Anforderung.
Ein weiterer Grund für Nicht-Idempotenz kann sein, dass das Anwenden der Änderung auf etwas anderes als die ursprüngliche Ressource die Ressource ungültig machen kann. Dies wäre dann auch der Fall, wenn Sie die Änderung mehrfach anwenden.

Bart van Ingen Schenau
quelle
3
Ich verstehe den letzten Absatz nicht. Können Sie ein Beispiel dafür geben, wie "das Anwenden der Änderung auf etwas anderes als die ursprüngliche Ressource die Ressource ungültig machen kann" und wie dies damit zusammenhängt, dass eine Änderung mehrmals auf dieselbe Ressource angewendet wird?
Robin Green
3
@RobinGreen: Angenommen, die Ressource wird in XML dargestellt, mit der Voraussetzung, dass höchstens ein <name>Element vorhanden ist. Wenn der PATCH <name>einer Ressource ein Element hinzufügt , das ursprünglich kein Element enthielt, wird die Ressource durch zweimaliges Anwenden des PATCH (oder durch Anwenden auf eine Ressource, die bereits ein Element enthält <name>) ungültig, da sie plötzlich zwei <name>Elemente enthält, die nicht zulässig sind für solche Ressourcen.
Bart van Ingen Schenau
13
Das erste Beispiel, das Sie angegeben haben, ist meiner Meinung nach nicht relevant, da es idempotent ist. Beispiel mit DELETE, das idempotent ist, erste Anforderung: Ressource existiert, wurde aber gelöscht (gibt 2xx zurück), zweite Anforderung: Ressource existiert nicht und existiert nach der Anforderung noch nicht, gibt 4xx zurück. Der Serverstatus hat sich nicht geändert, daher die Idempotenz. In Ihrem Beispiel erste Anforderung: PATCH von BenFra an JohDoe, Ressourcenname war BenFra, jetzt ist es JohDoe (gibt 2xx zurück), zweite Anforderung: PATCH von BenFra an JohDoe, Ressourcenname war JohDoe und ist immer noch JohDoe (gibt 4xx zurück). Dies zeigt also nicht, dass PATCH nicht übermächtig sein kann.
sp00m
Weitere Informationen finden Sie hier: stackoverflow.com/q/4088350/1225328
sp00m 18.11.16
8

Ich denke, klare Antwort, wenn PATCH nicht idempotent ist dieser Absatz aus RFC 5789:

Es gibt auch Fälle, in denen Patch-Formate nicht von einem bekannten Ausgangspunkt aus ausgeführt werden müssen (z. B. Anhängen von Textzeilen an Protokolldateien oder nicht kollidierende Zeilen an Datenbanktabellen). In diesem Fall ist die gleiche Sorgfalt bei Client-Anforderungen nicht erforderlich .

Da RFC angibt, dass der Patch einige "allgemeine Änderungen" an der Ressource enthält, sollten wir uns nicht nur mit dem Ersetzen von Feldern befassen. Wenn es sich bei der Ressource um einen Zähler handelt, kann der Patch das Inkrement anfordern, was eindeutig nicht idempotet ist.

Ivan
quelle
2

PATCHAnforderungen beschreiben eine Gruppe von Vorgängen, die auf eine Ressource angewendet werden sollen. Wenn Sie dieselbe Gruppe von Vorgängen zweimal auf dieselbe Ressource anwenden, ist das Ergebnis möglicherweise nicht dasselbe. Dies liegt daran, dass Sie die Operationen selbst definieren können. Mit anderen Worten, Sie müssen die Zusammenführungsregeln definieren .

Denken Sie daran, dass eine PATCHAnfrage zum Patchen von Ressourcen in vielen verschiedenen Formaten verwendet werden kann, nicht nur für JSON.

Eine PATCHAnfrage kann also idempotent sein, wenn Sie die Zusammenführungsregeln als idempotent definieren .

Idempotentes Beispiel:

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

// PATCH request
{
  age: 33
}

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

Nicht idempotentes Beispiel:

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

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

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

Im zweiten Beispiel habe ich eine "Mongo-ähnliche" Syntax verwendet, die ich für das Inkrementieren eines Attributs erfunden habe. Dies ist eindeutig nicht idempotent, da das mehrmalige Senden derselben Anfrage jedes Mal zu unterschiedlichen Ergebnissen führen würde.

Jetzt fragen Sie sich vielleicht, ob die Verwendung einer solchen Syntax gültig ist. Nach Standards ist es:

Der Unterschied zwischen den Anforderungen PUT und PATCH spiegelt sich in der Art und Weise wider, wie der Server die eingeschlossene Entität verarbeitet, um die durch den Anforderungs-URI angegebene Ressource zu ändern. In einer PUT-Anforderung wird die eingeschlossene Entität als geänderte Version der auf dem Ursprungsserver gespeicherten Ressource betrachtet, und der Client fordert das Ersetzen der gespeicherten Version an. Mit PATCH enthält die beigefügte Entität jedoch eine Reihe von Anweisungen, die beschreiben, wie eine Ressource, die sich derzeit auf dem Ursprungsserver befindet, geändert werden sollte, um eine neue Version zu erstellen.

Und vielleicht fragen Sie sich auch, ob es sich lohnt , PATCHAnfragen auf diese Weise zu verwenden, und viele Leute sind der Meinung , dass dies nicht der Fall ist. Hier finden Sie eine gute Antwort mit vielen Kommentaren zu diesem Problem.

Jbm
quelle