Angenommen, eine REST-API gibt als Antwort auf eine HTTP- GET
Anforderung einige zusätzliche Daten in einem Unterobjekt zurück owner
:
{
id: 'xyz',
... some other data ...
owner: {
name: 'Jo Bloggs',
role: 'Programmer'
}
}
Natürlich wollen wir nicht, dass jemand PUT
zurück kann
{
id: 'xyz',
... some other data ...
owner: {
name: 'Jo Bloggs',
role: 'CEO'
}
}
und haben das geschafft. Tatsächlich sind wir wahrscheinlich nicht gehen sogar implementieren einen Weg , dass sogar potenziell erfolgreich zu sein, in diesem Fall.
Bei dieser Frage geht es jedoch nicht nur um Unterobjekte: Was sollte im Allgemeinen mit Daten geschehen, die in einer PUT-Anforderung nicht geändert werden dürfen?
Sollte es erforderlich sein, in der PUT-Anforderung zu fehlen?
Sollte es stillschweigend weggeworfen werden?
Sollte es überprüft werden und wenn es vom alten Wert dieses Attributs abweicht, einen HTTP-Fehlercode in der Antwort zurückgeben?
Oder sollten wir RFC 6902-JSON-Patches verwenden, anstatt die gesamte JSON zu senden?
quelle
Antworten:
Es gibt weder in der W3C-Spezifikation noch in den inoffiziellen Regeln von REST eine Regel, die besagt, dass a
PUT
dasselbe Schema / Modell wie sein entsprechendes verwenden mussGET
.Es ist schön, wenn sie ähnlich sind , aber es ist nicht ungewöhnlich
PUT
, Dinge etwas anders zu machen. Ich habe zum Beispiel viele APIs gesehen, die der Einfachheit halber eine Art ID in den von a zurückgegebenen Inhalten enthaltenGET
. Aber mit aPUT
wird diese ID ausschließlich von der URI bestimmt und hat im Inhalt keine Bedeutung. Jede im Body gefundene ID wird unbemerkt ignoriert.REST und das Web im Allgemeinen sind stark an das Robustness-Prinzip gebunden : "Sei konservativ in dem, was du tust [send], sei liberal in dem, was du akzeptierst." Wenn Sie dem philosophisch zustimmen, liegt die Lösung auf der Hand: Ignorieren Sie ungültige Daten in
PUT
Anfragen. Dies gilt sowohl für unveränderliche Daten wie in Ihrem Beispiel als auch für tatsächlichen Unsinn, z. B. unbekannte Felder.PATCH
Dies ist möglicherweise eine weitere Option, die Sie jedoch nur implementieren sollten,PATCH
wenn Sie auch Teilaktualisierungen unterstützen.PATCH
bedeutet, nur die spezifischen Attribute zu aktualisieren, die ich in den Inhalt einbeziehe ; Dies bedeutet nicht , dass die gesamte Entität ersetzt wird, sondern dass einige bestimmte Felder ausgeschlossen werden . Worüber Sie tatsächlich sprechen, ist kein teilweises Update, sondern ein vollständiges Update, idempotent und alles, nur ein Teil der Ressource ist schreibgeschützt.Wenn Sie diese Option auswählen, können Sie eine 200 (OK) mit der tatsächlich aktualisierten Entität in der Antwort zurücksenden, damit die Clients klar erkennen, dass die schreibgeschützten Felder nicht aktualisiert wurden.
Es gibt sicherlich einige Leute , die anders denken - dass es ein Fehler sein sollte, zu versuchen, einen schreibgeschützten Teil einer Ressource zu aktualisieren. Es gibt eine Rechtfertigung dafür, hauptsächlich auf der Grundlage, dass Sie definitiv einen Fehler zurückgeben würden, wenn die gesamte Ressource schreibgeschützt wäre und der Benutzer versucht hätte, sie zu aktualisieren. Es verstößt definitiv gegen das Robustheitsprinzip, aber Sie könnten es als "selbstdokumentierend" für Benutzer Ihrer API ansehen.
Hierfür gibt es zwei Konventionen, die beide Ihren ursprünglichen Vorstellungen entsprechen, aber ich werde sie erweitern. Die erste besteht darin, zu verhindern, dass schreibgeschützte Felder im Inhalt angezeigt werden, und in diesem Fall HTTP 400 (Bad Request) zurückzugeben. APIs dieser Art sollten auch ein HTTP 400 zurückgeben, wenn andere nicht erkannte / nicht verwendbare Felder vorhanden sind. Die zweite besteht darin, dass die schreibgeschützten Felder mit dem aktuellen Inhalt identisch sein müssen und eine 409 (Konflikt) zurückgeben, wenn die Werte nicht übereinstimmen.
Ich mag die Gleichheitsprüfung mit 409 wirklich nicht, weil der Client immer eine
GET
ausführen muss, um die aktuellen Daten abzurufen, bevor er eine ausführen kannPUT
. Das ist einfach nicht schön und wird wahrscheinlich zu einer schlechten Leistung führen, für jemanden, irgendwo. Ich mag 403 (Verboten) auch wirklich nicht, da dies impliziert, dass die gesamte Ressource geschützt ist, nicht nur ein Teil davon. Wenn Sie also unbedingt validieren müssen, anstatt dem Robustheitsprinzip zu folgen, validieren Sie alle Ihre Anforderungen und geben Sie eine 400 für alle mit zusätzlichen oder nicht beschreibbaren Feldern zurück.Vergewissern Sie sich, dass Ihre 400/409 / whatever Informationen über das jeweilige Problem und dessen Behebung enthält.
Beide Ansätze sind gültig, aber ich bevorzuge den ersteren, um dem Robustheitsprinzip zu entsprechen. Wenn Sie schon einmal mit einer großen REST-API gearbeitet haben, werden Sie den Wert der Abwärtskompatibilität zu schätzen wissen. Wenn Sie jemals ein vorhandenes Feld entfernen oder schreibgeschützt machen möchten, handelt es sich um eine abwärtskompatible Änderung, wenn der Server diese Felder einfach ignoriert und alte Clients weiterhin funktionieren. Wenn Sie den Inhalt jedoch streng überprüfen, ist er nicht mehr abwärtskompatibel und alte Clients funktionieren nicht mehr. Ersteres bedeutet im Allgemeinen weniger Arbeit für den Betreuer einer API und seine Clients.
quelle
Idem Potenz
Nach dem RFC müsste ein PUT das gesamte Objekt an die Ressource liefern. Der Hauptgrund dafür ist, dass PUT idempotent sein sollte. Dies bedeutet, dass eine wiederholte Anforderung auf dem Server zu demselben Ergebnis führen sollte.
Wenn Sie Teilaktualisierungen zulassen, kann es nicht mehr idem-potent sein. Wenn Sie zwei Kunden haben. Client A und B, dann kann das folgende Szenario entstehen:
Client A erhält ein Bild von Ressourcenbildern. Dies enthält eine Beschreibung des Bildes, die noch gültig ist. Der Client B erstellt ein neues Image und aktualisiert die Beschreibung entsprechend. Das Bild hat sich geändert. Kunde A sieht, er muss die Beschreibung nicht ändern, weil es so ist, wie er es wünscht und nur das Bild setzt.
Dies führt zu einer Inkonsistenz, das Bild hat die falschen Metadaten angehängt!
Noch ärgerlicher ist, dass jeder Vermittler die Anfrage wiederholen kann. Falls sich herausstellt, dass der PUT fehlgeschlagen ist.
Die Bedeutung von PUT kann nicht geändert werden (obwohl Sie es missbrauchen können).
Andere Optionen
Zum Glück gibt es noch eine andere Option: PATCH. PATCH ist eine Methode, mit der Sie eine Struktur teilweise aktualisieren können. Sie können einfach eine Teilstruktur senden. Für einfache Anwendungen ist dies in Ordnung. Es wird nicht garantiert, dass diese Methode gleich wirksam ist. Der Kunde sollte eine Anfrage in der folgenden Form senden:
Der Server kann mit 204 (ohne Inhalt) antworten, um den Erfolg zu melden. Im Fehlerfall können Sie einen Teil der Struktur nicht aktualisieren. Die PATCH-Methode ist atomar.
Der Nachteil dieser Methode ist, dass dies nicht von allen Browsern unterstützt wird. Dies ist jedoch die natürlichste Option in einem REST-Service.
Beispiel für eine Patch-Anforderung: http://tools.ietf.org/html/rfc5789#section-2.1
Json Patchen
Die json-Option scheint ziemlich umfassend und eine interessante Option zu sein. Die Implementierung für Dritte kann jedoch schwierig sein. Sie müssen sich entscheiden, ob Ihre Benutzerbasis damit umgehen kann.
Es ist auch etwas kompliziert, da Sie einen kleinen Interpreter erstellen müssen, der die Befehle in eine Teilstruktur konvertiert, die Sie zum Aktualisieren Ihres Modells verwenden werden. Dieser Interpreter sollte auch prüfen, ob die angegebenen Befehle sinnvoll sind. Einige Befehle heben sich gegenseitig auf. (Feld schreiben, Feld löschen). Ich denke, Sie möchten dies dem Client zurückmelden, um die Debug-Zeit auf seiner Seite zu begrenzen.
Aber wenn Sie die Zeit haben, ist dies eine wirklich elegante Lösung. Sie sollten die Felder natürlich trotzdem validieren. Sie können dies mit der PATCH-Methode kombinieren, um im REST-Modell zu bleiben. Aber ich denke POST wäre hier akzeptabel.
Es wird schlecht
Wenn Sie sich für die Option PUT entscheiden, ist dies etwas riskant. Dann sollten Sie den Fehler zumindest nicht verwerfen. Der Benutzer hat eine gewisse Erwartung (die Daten werden aktualisiert) und wenn Sie dies brechen, werden Sie einigen Entwicklern keine gute Zeit geben.
Sie können wählen, ob Sie zurückweisen möchten: 409 Conflict oder 403 Forbidden. Es hängt davon ab, wie Sie den Aktualisierungsprozess betrachten. Wenn Sie es als eine Reihe von Regeln sehen (systemzentriert), wird der Konflikt schöner sein. Diese Felder können nicht aktualisiert werden. (Im Widerspruch zu den Regeln). Wenn Sie es als Autorisierungsproblem ansehen (benutzerzentriert), sollten Sie verboten zurückkehren. Mit: Sie sind nicht berechtigt, diese Felder zu ändern.
Sie sollten Benutzer dennoch zwingen, alle änderbaren Felder zu senden.
Eine sinnvolle Möglichkeit, dies durchzusetzen, besteht darin, sie auf eine Subressource festzulegen, die nur die veränderbaren Daten anbietet.
Persönliche Meinung
Persönlich würde ich mich für das einfache PATCH-Modell entscheiden (wenn Sie nicht mit Browsern arbeiten müssen) und es später mit einem JSON-Patch-Prozessor erweitern. Dies kann durch Unterscheidung der Mimetypen erfolgen: Der Mime-Typ des Json-Patches:
Anwendung / Json-Patch
Und json: application / json-patch
macht es einfach, es in zwei Phasen zu implementieren.
quelle