it-swarm.com.de

Wie sollte eine REST API) PUT-Anforderungen an teilweise veränderbare Ressourcen verarbeiten?

Angenommen, eine REST API) gibt als Antwort auf eine HTTP GET -Anforderung einige zusätzliche Daten in einem Unterobjekt owner zurück:

{
  id: 'xyz',
  ... some other data ...
  owner: {
    name: 'Jo Bloggs',
    role: 'Programmer'
  }
}

Natürlich wollen wir nicht, dass jemand in der Lage ist, PUT zurück zu kommen

{
  id: 'xyz',
  ... some other data ...
  owner: {
    name: 'Jo Bloggs',
    role: 'CEO'
  }
}

und das gelingt. In der Tat werden wir wahrscheinlich nicht einmal implementieren einen Weg finden, damit dies in diesem Fall sogar potenziell erfolgreich ist.

Bei dieser Frage geht es jedoch nicht nur um Unterobjekte: Was ist im Allgemeinen mit Daten zu tun, die in einer PUT-Anforderung nicht geändert werden dürfen?

Sollte es erforderlich sein, dass es in der PUT-Anforderung fehlt?

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 den gesamten JSON zu senden?

50
Robin Green

Es gibt weder in der W3C-Spezifikation noch in den inoffiziellen Regeln von REST eine Regel, die besagt, dass ein PUT dasselbe Schema/Modell wie sein entsprechendes GET verwenden muss.

Es ist schön, wenn sie ähnlich sind, aber es ist nicht ungewöhnlich, dass PUT die Dinge etwas anders macht. Zum Beispiel habe ich viele APIs gesehen, die der Einfachheit halber eine Art ID in dem Inhalt enthalten, der von einem GET zurückgegeben wird. Bei einem PUT wird diese ID jedoch ausschließlich vom URI bestimmt und hat im Inhalt keine Bedeutung. Jede im Körper gefundene ID wird stillschweigend ignoriert.

REST und das Web im Allgemeinen sind stark an das Robustness Principle : "Seien Sie konservativ bei dem, was Sie tun [senden], seien Sie liberal bei dem, was Sie akzeptieren." If 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. unbekannte Felder.

PATCH ist möglicherweise eine weitere Option, aber Sie sollten PATCH nur implementieren, wenn Sie tatsächlich Teilaktualisierungen unterstützen. PATCH bedeutet aktualisiere nur die spezifischen Attribute, die ich in den Inhalt aufgenommen habe; es bedeutet nicht die gesamte Entität ersetzen, aber einige bestimmte Felder ausschließen. Worüber Sie tatsächlich sprechen, ist nicht wirklich ein Teil-Update, es ist ein vollständiges Update, idempotent und alles, es ist nur so, dass ein Teil der Ressource schreibgeschützt ist.

Eine gute Sache, wenn Sie diese Option wählen, wäre, eine 200 (OK) mit der tatsächlich aktualisierten Entität in der Antwort zurückzusenden, damit Clients die schreibgeschützten Felder deutlich sehen können wurden nicht aktualisiert.

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 Ressource gesamt 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 für Benutzer Ihrer API als "selbstdokumentierender" betrachten.

Hierfür gibt es zwei Konventionen, die beide Ihren ursprünglichen Ideen entsprechen, aber ich werde sie erweitern. Die erste besteht darin, zu verhindern, dass schreibgeschützte Felder im Inhalt angezeigt werden, und in diesem Fall ein HTTP 400 (Bad Request) zurückzugeben. APIs dieser Art sollten auch ein HTTP 400 zurückgeben, wenn andere nicht erkannte/unbrauchbare 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ückzugeben, wenn die Werte nicht übereinstimmen.

Ich mag die Gleichheitsprüfung mit 409 wirklich nicht, weil der Client immer ein GET ausführen muss, um die aktuellen Daten abzurufen, bevor er ein PUT ausführen kann. Das ist einfach nicht schön und wird wahrscheinlich zu einer schlechten Leistung für jemanden irgendwo führen. Ich mag auch wirklich 403 (Verboten) dafür nicht, da dies impliziert, dass die Ressource gesamt geschützt ist, nicht nur ein Teil davon. Meine Meinung ist also, wenn Sie unbedingt validieren müssen, anstatt dem Robustheitsprinzip zu folgen, validieren Sie all Ihrer Anfragen und geben Sie eine 400 für alle zurück, die zusätzliche oder nicht beschreibbare Felder haben.

Stellen Sie sicher, dass Ihr 400/409/was auch immer Informationen darüber enthält, was das spezifische Problem ist und wie es behoben werden kann.

Beide Ansätze sind gültig, aber ich bevorzuge den ersteren im Einklang mit dem Robustheitsprinzip. Wenn Sie jemals Erfahrung mit mit einer großen REST API) hatten, werden Sie den Wert der Abwärtskompatibilität zu schätzen wissen. Wenn Sie sich jemals entscheiden, ein vorhandenes Feld zu entfernen oder schreibgeschützt machen, es ist eine abwärtskompatible Änderung, wenn der Server diese Felder einfach ignoriert und alte Clients weiterhin funktionieren. Wenn Sie jedoch eine strikte Überprüfung des Inhalts durchführen, ist dies nicht = abwärtskompatibel und alte Clients funktionieren nicht mehr. Ersteres bedeutet im Allgemeinen weniger Arbeit für den Betreuer einer API und ihre Clients.

51
Aaronaught

Idem Potenz

Nach dem RFC müsste ein PUT ein vollständiges Objekt an die Ressource liefern. Der Hauptgrund dafür ist, dass PUT idempotent sein sollte. Dies bedeutet, dass eine Anforderung, die wiederholt wird, auf dem Server das gleiche Ergebnis erzielen sollte.

Wenn Sie Teilaktualisierungen zulassen, kann dies nicht mehr idem-potent sein. Wenn Sie zwei Kunden haben. Client A und B, dann kann sich das folgende Szenario entwickeln:

Client A erhält ein Bild aus Ressourcenbildern. Diese enthält eine Beschreibung des Bildes, die noch gültig ist. Der Client B legt ein neues Bild an 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 setzen.

Dies führt zu einer Inkonsistenz, an das Bild sind die falschen Metadaten angehängt!

Noch ärgerlicher ist, dass jeder Vermittler die Anfrage wiederholen kann. Falls es irgendwie entscheidet, 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, dies ist 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 ist nicht garantiert, dass diese Methode sehr wirksam ist. Der Kunde sollte eine Anfrage in folgender Form senden:

PATCH /file.txt HTTP/1.1
Host: www.example.com
Content-Type: application/example
If-Match: "e0023aa4e"
Content-Length: 20
{fielda: 1, fieldc: 2}

Und der Server kann mit 204 (kein Inhalt) antworten, um den Erfolg zu kennzeichnen. Bei einem Fehler 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-Anfrage :http://tools.ietf.org/html/rfc5789#section-2.1

Json-Patching

Die json-Option scheint ziemlich umfassend und eine interessante Option zu sein. Die Implementierung für Dritte kann jedoch schwierig sein. Sie müssen 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 bereitgestellten Befehle sinnvoll sind. Einige Befehle heben sich gegenseitig auf. (schreibe fielda, lösche fielda). Ich denke, Sie möchten dies dem Client zurückmelden, um die Debug-Zeit auf seiner Seite zu begrenzen.

Aber wenn Sie Zeit haben, ist dies eine wirklich elegante Lösung. Sie sollten die Felder natürlich noch validieren. Sie können dies mit der PATCH-Methode kombinieren, um im Modell REST) zu bleiben. Aber ich denke, POST wäre hier akzeptabel.

Schlecht werden

Wenn Sie sich für die PUT-Option 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 diese brechen, werden Sie einigen Entwicklern keine gute Zeit geben.

Sie können wählen, ob Sie zurückmelden 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), ist der Konflikt schöner. 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 weiterhin zwingen, alle veränderbaren Felder zu senden.

Eine vernünftige Option, um dies durchzusetzen, besteht darin, es auf eine Unterressource festzulegen, die nur die veränderbaren Daten bietet.

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 Differenzieren der Mimetypen erfolgen: Der MIME-Typ des JSON-Patches:

application/json-patch

Und json: application/json-patch

macht es einfach, es in zwei Phasen zu implementieren.

10
Edgar Klerks