Ich erstelle eine API, in der der Benutzer den Server auffordern kann, mehrere Aktionen in einer HTTP-Anforderung auszuführen. Das Ergebnis wird als JSON-Array mit einem Eintrag pro Aktion zurückgegeben.
Jede dieser Aktionen kann unabhängig voneinander fehlschlagen oder erfolgreich sein. Beispielsweise ist die erste Aktion möglicherweise erfolgreich, die Eingabe für die zweite Aktion ist möglicherweise schlecht formatiert und kann nicht überprüft werden, und die dritte Aktion verursacht möglicherweise einen unerwarteten Fehler.
Wenn es eine Anfrage pro Aktion gäbe, würde ich die Statuscodes 200, 422 bzw. 500 zurückgeben. Aber jetzt, wo es nur eine Anfrage gibt, welchen Statuscode soll ich zurückgeben?
Einige Optionen:
- Geben Sie immer 200 zurück und geben Sie detailliertere Informationen im Körper an.
- Befolgen Sie die obige Regel möglicherweise nur, wenn die Anforderung mehr als eine Aktion enthält?
- Vielleicht 200 zurückgeben, wenn alle Anfragen erfolgreich sind, andernfalls 500 (oder ein anderer Code)?
- Verwenden Sie einfach eine Anforderung pro Aktion und akzeptieren Sie den zusätzlichen Aufwand.
- Etwas ganz anderes?
Antworten:
Die kurze, direkte Antwort
Da die Anforderung von der Ausführung der Aufgabenliste spricht (Aufgaben sind die Ressource, von der wir hier sprechen), ist es sinnvoll, wenn die Aufgabengruppe zur Ausführung verschoben wurde (dh unabhängig vom Ausführungsergebnis) dass der Antwortstatus sein wird
200 OK
. Wenn andernfalls ein Problem aufgetreten ist, das die Ausführung der Aufgabengruppe verhindert, z. B. ein Fehler bei der Validierung der Aufgabenobjekte , oder ein erforderlicher Dienst nicht verfügbar ist, sollte der Antwortstatus diesen Fehler anzeigen. Wenn die Ausführung der Tasks beginnt, da die auszuführenden Tasks im Anforderungshauptteil aufgeführt sind, würde ich davon ausgehen, dass die Ausführungsergebnisse im Antworthauptteil aufgeführt werden.Die lange, philosophische Antwort
Dieses Dilemma tritt auf, weil Sie von dem abweichen, für das HTTP entwickelt wurde. Sie interagieren nicht mit ihm, um Ressourcen zu verwalten, sondern verwenden ihn als Mittel zum Aufrufen von Remotemethoden (was nicht sonderbar ist, jedoch ohne ein vorgefasstes Schema schlecht funktioniert).
Mit dem oben Gesagten und ohne den Mut, diese Antwort in einen langmeinenden Leitfaden zu verwandeln, ist das Folgende ein URI-Schema, das einem Ressourcenmanagement-Ansatz entspricht:
/tasks
GET
listet alle Aufgaben auf, paginiertPOST
Fügt eine einzelne Aufgabe hinzu/tasks/task/[id]
GET
antwortet mit dem Statusobjekt einer einzelnen AufgabeDELETE
bricht eine Aufgabe ab oder löscht sie/tasks/groups
GET
listet alle Aufgabengruppen auf, paginiertPOST
Fügt eine Gruppe von Aufgaben hinzu/tasks/groups/group/[id]
GET
antwortet mit dem Status einer AufgabengruppeDELETE
Löscht / löscht die AufgabengruppeIn dieser Struktur geht es um Ressourcen, nicht darum, was man mit ihnen machen soll. Was mit Ressourcen gemacht wird, ist das Anliegen eines anderen Dienstes.
Ein weiterer wichtiger Punkt ist, dass es ratsam ist, in einem HTTP-Request-Handler nicht zu lange zu blockieren. Ähnlich wie bei der Benutzeroberfläche sollte eine HTTP-Schnittstelle reagieren - in einem Zeitrahmen, der um einige Größenordnungen langsamer ist (da sich diese Schicht mit E / A befasst).
Der Schritt zum Entwerfen einer HTTP-Schnittstelle, mit der Ressourcen streng verwaltet werden, ist wahrscheinlich so schwierig wie das Entfernen der Arbeit von einem UI-Thread, wenn auf eine Schaltfläche geklickt wird. Es ist erforderlich, dass der HTTP-Server mit anderen Diensten kommuniziert, um Aufgaben auszuführen, anstatt sie im Anforderungshandler auszuführen. Dies ist keine flache Implementierung, sondern eine Richtungsänderung.
Einige Beispiele für die Verwendung eines solchen URI-Schemas
Ausführen einer einzelnen Aufgabe und Verfolgen des Fortschritts:
POST /tasks
mit der auszuführenden AufgabeGET /tasks/task/[id]
bis das Antwortobjektcompleted
einen positiven Wert hat und gleichzeitig den aktuellen Status / Fortschritt anzeigtEine einzelne Aufgabe ausführen und auf ihren Abschluss warten:
POST /tasks
mit der auszuführenden AufgabeGET /tasks/task/[id]?awaitCompletion=true
tillcompleted
hat einen positiven Wert (wahrscheinlich hat es ein Timeout, weshalb dies eine Schleife sein sollte)Ausführen einer Aufgabengruppe und Verfolgen des Fortschritts:
POST /tasks/groups
mit der Gruppe der auszuführenden AufgabenGET /tasks/groups/group/[groupId]
bis diecompleted
Eigenschaft des Antwortobjekts einen Wert hat, der den Status der einzelnen Aufgabe anzeigt (3 von beispielsweise 5 Aufgaben erledigt)Anfordern einer Ausführung für eine Aufgabengruppe und Warten auf deren Fertigstellung:
POST /tasks/groups
mit der Gruppe der auszuführenden AufgabenGET /tasks/groups/group/[groupId]?awaitCompletion=true
bis antwortet mit einem Ergebnis, das den Abschluss anzeigt (wahrscheinlich hat es eine Zeitüberschreitung, weshalb eine Schleife ausgeführt werden sollte)quelle
Meine Stimme wäre, diese Aufgaben in getrennte Anträge aufzuteilen. Wenn jedoch zu viele Roundtrips ein Problem darstellen, bin ich auf den HTTP-Antwortcode 207 - Multi-Status gestoßen
Kopieren / Einfügen von diesem Link:
quelle
207
scheint das zu sein, was das OP will, aber ich möchte wirklich betonen, dass es wahrscheinlich eine schlechte Idee ist, diesen Multi-Request-in-One-Ansatz zu verfolgen. Wenn es um die Leistung geht, sollten Sie eine Architektur für horizontal skalierbare Systeme im Cloud-Stil entwickeln (was HTTP-basierte Systeme hervorragend können)Obwohl Multi-Status eine Option ist, würde ich 200 (alles in Ordnung) zurückgeben, wenn alle Anforderungen erfolgreich waren, und andernfalls einen Fehler (500 oder vielleicht 207).
Der Standardfall sollte in der Regel 200 sein - alles funktioniert. Und Kunden sollten das nur überprüfen müssen. Und nur wenn der Fehlerfall aufgetreten ist, können Sie eine 500 (oder eine 207) zurückgeben. Ich denke, der 207 ist eine gültige Wahl im Fall von mindestens einem Fehler, aber wenn Sie das gesamte Paket als eine Transaktion sehen, können Sie auch 500 senden. - Der Client möchte die Fehlermeldung so oder so interpretieren.
Warum nicht immer 207 senden? - Weil Standardfälle einfach und Standard sein sollten. Während Ausnahmefälle außergewöhnlich sein können. Ein Kunde sollte den Antworttext nur lesen und weitere komplexe Entscheidungen treffen müssen, wenn eine Ausnahmesituation dies rechtfertigt.
quelle
Eine Möglichkeit wäre, immer einen Statuscode 200 und dann bestimmte Fehler in Ihrem JSON-Dokumentkörper zurückzugeben. Genau so sind einige APIs konzipiert (sie geben immer einen Statuscode 200 zurück und lösen den Fehler im Hauptteil aus). Weitere Informationen zu den verschiedenen Ansätzen finden Sie unter http://archive.oreilly.com/pub/post/restful_error_handling.html
quelle
200
zu kennzeichnen, dass alles in Ordnung ist, die Anforderung eingegangen ist und gültig war , und dann mit dem JSON Details zu dem bereitzustellen, was in dieser Anforderung passiert ist (dh das Ergebnis der Transaktionen).Ich denke neilsimp1 ist korrekt, aber ich würde eine Neugestaltung der gesendeten Daten empfehlen, so dass Sie später eine senden
206 - Accepted
und die Daten verarbeiten können. Vielleicht mit Rückrufen.Das Problem beim Versuch, mehrere Aktionen in einer einzigen Anfrage zu senden, ist genau die Tatsache, dass jede Aktion einen eigenen "Status" haben sollte.
Betrachtet man den Import einer CSV (ich weiß nicht wirklich, worum es im OP geht, aber es ist eine einfache Version). POSTEN Sie die CSV und erhalten Sie eine 206 zurück. Später kann die CSV importiert werden und Sie können den Status des Imports mit einem GET (200) gegen eine URL abrufen, die pro Zeile Fehler anzeigt.
Das gleiche Muster kann auf viele Batch-Operationen angewendet werden
Der Code, der den POST verarbeitet, muss nur überprüfen, ob das Format der Betriebsdaten gültig ist. Dann können die Operationen zu einem späteren Zeitpunkt ausgeführt werden. In einem Hintergrundarbeiter können Sie so beispielsweise einfacher skalieren. Dann können Sie den Status der Vorgänge jederzeit überprüfen. Sie können Polling oder Callbacks oder Streams oder was auch immer verwenden, um die Notwendigkeit zu erkennen, wann eine Reihe von Vorgängen abgeschlossen ist.
quelle
Hier gibt es schon viele gute Antworten, aber ein Aspekt fehlt:
Was ist der Vertrag, den Ihre Kunden erwarten?
HTTP-Returncodes sollten zumindest eine Erfolgs- / Misserfolgsunterscheidung anzeigen und somit die Rolle der "Ausnahmen armer Männer" spielen. Dann bedeutet 200 "Vertrag vollständig erfüllt" und 4xx oder 5xx bedeuten Nichterfüllung.
Naiv würde ich erwarten, dass der Vertrag Ihrer Mehrfachaktionsanforderung "alle meine Aufgaben erledigen" lautet, und wenn eine von ihnen fehlschlägt, war die Anforderung nicht (vollständig) erfolgreich. Normalerweise würde ich als Kunde 200 als "alles in Ordnung" verstehen, und Codes aus der 400er- und 500er-Familie zwingen mich, über die Folgen eines (teilweisen) Ausfalls nachzudenken. Verwenden Sie also 200 für "Alle Aufgaben erledigt" und 500 plus eine beschreibende Antwort für den Fall eines teilweisen Ausfalls.
Ein anderer hypothetischer Vertrag könnte sein, "alle Aktionen zu versuchen". Dann ist es völlig vertragsgemäß, wenn (einige) Aktionen fehlschlagen. Sie würden also immer 200 plus ein Ergebnisdokument zurückgeben, in dem Sie die Erfolgs- / Fehlerinformationen für die einzelnen Aufgaben finden.
Welchem Vertrag möchten Sie also folgen? Beide sind gültig, aber der erste (200, nur wenn alles erledigt wurde) ist für mich intuitiver und entspricht besser den typischen Softwaremustern. Und für die (hoffentlich) meisten Fälle, in denen der Service alle Aufgaben erledigt hat, ist es für den Kunden unkompliziert, diesen Fall zu erkennen.
Ein letzter wichtiger Aspekt: Wie teilen Sie Ihren Kunden Ihre Vertragsentscheidung mit? In Java würde ich beispielsweise Methodennamen wie "doAll ()" oder "tryToDoAll ()" verwenden. In HTTP können Sie die Endpunkt-URLs entsprechend benennen, in der Hoffnung, dass Ihre Client-Entwickler die Benennung sehen, lesen und verstehen (darauf würde ich nicht wetten). Ein Grund mehr, sich für den Vertrag mit der geringsten Überraschung zu entscheiden.
quelle
Antworten:
Ein Statuscode beschreibt den Status eines Vorgangs. Daher ist es sinnvoll, eine Operation pro Anforderung durchzuführen.
Mehrere unabhängige Operationen unterbrechen das Prinzip, auf dem das Anforderungs-Antwort-Modell und die Statuscodes basieren. Du kämpfst gegen die Natur.
HTTP / 1.1 und HTTP / 2 haben den Aufwand für HTTP-Anforderungen erheblich verringert. Ich schätze, es gibt sehr wenige Situationen, in denen es ratsam ist, unabhängige Anfragen zu stapeln.
Das gesagt,
(1) Mit einer PATCH-Anforderung ( RFC 5789 ) können Sie mehrere Änderungen vornehmen . Dies setzt jedoch voraus, dass die Änderungen nicht eigenständig sind; Sie werden atomar angewendet (alles oder nichts).
(2) Andere haben auf den 207 Multi-Status-Code hingewiesen. Dies ist jedoch nur für WebDAV ( RFC 4918 ), eine Erweiterung von HTTP, definiert.
Eine 207-WebDAV-XML-Antwort wäre in einer Nicht-WebDAV-API so seltsam wie eine Ente. Mach das nicht.
quelle
Wenn Sie wirklich mehrere Aktionen in einer Anfrage haben müssen, warum nicht alle Aktionen in eine Transaktion im Backend einbinden? Auf diese Weise sind entweder alle erfolgreich oder alle scheitern.
Als Client, der die API verwendet, kann ich bei einem API-Aufruf den vollständigen Erfolg oder Misserfolg verkraften. Teilerfolge sind schwer zu bewältigen, da ich mit allen möglichen Folgezuständen umgehen müsste.
quelle