HTTP-Statuscode für "Wird noch verarbeitet"

47

Ich erstelle eine RESTful-API, die das Einreihen von Aufgaben mit langer Laufzeit für die spätere Bearbeitung unterstützt.

Der typische Workflow für diese API wäre:

  1. Benutzer füllt Formular aus
  2. Client sendet Daten an API
  3. API gibt 202 Accepted zurück
  4. Der Client leitet den Benutzer zu einer eindeutigen URL für diese Anforderung weiter ( /results/{request_id})
  5. ~ Irgendwann ~
  6. Der Client besucht die URL erneut und sieht die Ergebnisse auf dieser Seite.

Mein Problem tritt bei Schritt 6 auf. Jedes Mal, wenn ein Benutzer die Seite besucht, reiche ich eine Anfrage an meine API ( GET /api/results/{request_id}) ein. Im Idealfall ist die Aufgabe jetzt abgeschlossen, und ich würde 200 OK mit den Ergebnissen ihrer Aufgabe zurückgeben.

Aber Benutzer sind aufdringlich, und ich erwarte viele übereifrige Aktualisierungen, wenn das Ergebnis noch nicht verarbeitet ist.

Was ist meine beste Option für einen Statuscode, um Folgendes anzuzeigen:

  • diese Anfrage existiert,
  • es ist noch nicht fertig
  • aber es hat auch nicht versagt.

Ich erwarte nicht, dass ein einzelner Code all das kommuniziert, aber ich hätte gerne etwas, mit dem ich Metadaten weitergeben kann, anstatt dass der Client Inhalte erwartet.

Es könnte sinnvoll sein, einen 202 zurückzugeben, da dies hier keine andere Bedeutung hätte: Es ist eine GETAnfrage, daher wird möglicherweise nichts "akzeptiert". Wäre das eine vernünftige Wahl?

Die naheliegende Alternative zu all dem - was funktioniert, aber einen Zweck von Statuscodes zunichte macht - wäre, immer die Metadaten einzuschließen:

200 OK

{
    status: "complete",
    data: {
        foo: "123"
    }
}

...oder...

200 OK

{
    status: "pending"
}

Dann clientseitige, würde ich (seufzt) switchauf , response.data.statusum zu bestimmen , ob die Anforderung abgeschlossen wurde.

Sollte ich das tun? Oder gibt es eine bessere Alternative? Das fühlt sich für mich einfach so Web 1.0 an.

Matthew Haugen
quelle
1
Werden 1xx-Codes nicht genau für diesen Zweck erstellt?
Andy
@Andy Ich habe mir 102 angesehen, aber das ist für WebDAV-Sachen. Darüber hinaus, nein ... Sie sind hauptsächlich für die Kommunikation unterwegs. Nützlich beim Umstieg auf Web Sockets und so weiter.
Matthew Haugen
Was für eine Verzögerung redest du? 10 Sekunden? Oder 6 Stunden? Wenn die Verzögerungen kurz sind und sich im Allgemeinen innerhalb desselben Browserbesuchs befinden, führen Sie möglicherweise lange Abfragen oder Web-Sockets durch, anstatt regelmäßige Abfragen durchzuführen.
GrandmasterB
@GrandmasterB Möglicherweise sind es Stunden. Ich bin nicht für die Auftragsabwicklung selbst verantwortlich, daher habe ich keine wirklich gute Schätzung, aber es wird eine Weile dauern. Ansonsten lasse ich nur die erste POSTAnfrage offen. Das Hauptproblem bei langen Abfragen oder Web-Sockets ist, dass der Benutzer möglicherweise den Browser schließt und zurückkehrt. Ich könnte sie zu diesem Zeitpunkt wieder öffnen (und das ist es, was ich tue), aber es scheint sauberer zu sein, eine einzige API aufzurufen, bevor ich diese Sockets öffne, da es ein Vorteil ist, wenn dieses Problem auftritt.
Matthew Haugen

Antworten:

48

HTTP 202 akzeptiert (HTTP / 1.1)

Sie suchen nach HTTP 202 AcceptedStatus. Siehe RFC 2616 :

Die Anforderung wurde zur Verarbeitung angenommen, die Verarbeitung wurde jedoch nicht abgeschlossen.

HTTP 102-Verarbeitung (WebDAV)

RFC 2518 schlägt vor, Folgendes zu verwenden HTTP 102 Processing:

Der Statuscode 102 (Verarbeitung) ist eine Zwischenantwort, mit der dem Client mitgeteilt wird, dass der Server die vollständige Anforderung akzeptiert, jedoch noch nicht abgeschlossen hat.

aber es hat eine Einschränkung:

Der Server MUSS eine endgültige Antwort senden, nachdem die Anforderung abgeschlossen wurde.

Ich bin mir nicht sicher, wie ich den letzten Satz interpretieren soll. Sollte der Server während der Verarbeitung das Senden von Daten vermeiden und erst nach Abschluss der Verarbeitung antworten ? Oder erzwingt es nur, die Antwort zu beenden, wenn die Verarbeitung beendet wird? Dies kann hilfreich sein, wenn Sie den Fortschritt melden möchten. Senden Sie HTTP 102 und leeren Sie die Antwort Byte für Byte (oder Zeile für Zeile).

Für einen langen, aber linearen Prozess können Sie beispielsweise einhundert Punkte senden, die nach jedem Zeichen rot werden. Wenn die Clientseite (z. B. eine JavaScript-Anwendung) weiß, dass sie genau 100 Zeichen erwarten sollte, kann sie diese mit einem Fortschrittsbalken abgleichen, der dem Benutzer angezeigt wird.

Ein weiteres Beispiel betrifft einen Prozess, der aus mehreren nichtlinearen Schritten besteht. Nach jedem Schritt können Sie eine Protokollmeldung löschen, die dem Benutzer möglicherweise angezeigt wird, damit der Endbenutzer weiß, wie der Prozess abläuft.

Probleme mit fortschreitender Spülung

Beachten Sie, dass diese Technik zwar ihre Vorzüge hat, ich sie jedoch nicht empfehlen würde . Einer der Gründe dafür ist, dass die Verbindung offen bleiben muss, was sich negativ auf die Verfügbarkeit des Dienstes auswirken kann und sich nicht gut skalieren lässt.

Ein besserer Ansatz ist es, mit zu antworten HTTP 202 Acceptedund entweder den Benutzer später auf Sie zurückkommen zu lassen, um festzustellen, ob die Verarbeitung beendet wurde (z. B. durch wiederholtes Aufrufen einer bestimmten URI, /process/resultdie bis zum Abschluss des Prozesses mit HTTP 404 Not Found oder HTTP 409 Conflict antworten würde ) beendet und das Ergebnis ist fertig), oder benachrichtigen Sie den Benutzer, wenn die Verarbeitung abgeschlossen ist, wenn Sie den Client beispielsweise über einen Message Queue-Service ( Beispiel ) oder über WebSockets zurückrufen können.

Praktisches Beispiel

Stellen Sie sich einen Webdienst vor, der Videos konvertiert. Der Einstiegspunkt ist:

POST /video/convert

Das nimmt eine Videodatei von der HTTP-Anfrage und macht etwas Magie damit. Stellen wir uns vor, die Magie ist CPU-intensiv und kann daher während der Übertragung der Anforderung nicht in Echtzeit ausgeführt werden. Dies bedeutet, dass der Server nach dem Übertragen der Datei mit einem HTTP 202 AcceptedJSON-Inhalt antwortet. Dies bedeutet: „Ja, ich habe Ihr Video und arbeite daran. es wird irgendwann in der Zukunft fertig sein und unter der ID 123 erhältlich sein. “

Der Client hat die Möglichkeit, eine Nachrichtenwarteschlange zu abonnieren, um benachrichtigt zu werden, wenn die Verarbeitung abgeschlossen ist. Sobald es fertig ist, kann der Client das verarbeitete Video herunterladen. Gehen Sie dazu zu:

GET /video/download/123

was zu einer HTTP 200.

Was passiert, wenn der Client diesen URI abfragt, bevor er die Benachrichtigung erhält? Nun, der Server antwortet mit, HTTP 404da das Video noch nicht existiert. Es kann derzeit vorbereitet werden. Möglicherweise wurde es nie angefordert. Es kann einige Zeit in der Vergangenheit existieren und später entfernt werden. Wichtig ist nur, dass das resultierende Video nicht verfügbar ist.

Was ist nun, wenn sich der Kunde nicht nur um das endgültige Video, sondern auch um den Fortschritt kümmert (was noch wichtiger wäre, wenn es keinen Message Queue Service oder einen ähnlichen Mechanismus gibt)?

In diesem Fall können Sie einen anderen Endpunkt verwenden:

GET /video/status/123

was zu einer ähnlichen Antwort führen würde:

HTTP 200
{
    "id": 123,
    "status": "queued",
    "priority": 2,
    "progress-percent": 0,
    "submitted-utc-time": "2016-04-19T13:59:22"
}

Wenn Sie die Anforderung immer wieder ausführen, wird der Fortschritt angezeigt, bis Folgendes zutrifft:

HTTP 200
{
    "id": 123,
    "status": "done",
    "progress-percent": 100,
    "submitted-utc-time": "2016-04-19T13:59:22"
}

Es ist wichtig, einen Unterschied zwischen diesen drei Arten von Anfragen zu machen:

  • POST /video/convertStellt eine Aufgabe in die Warteschlange. Es sollte nur einmal aufgerufen werden: Ein erneuter Aufruf würde eine zusätzliche Aufgabe in die Warteschlange stellen.
  • GET /video/download/123betrifft das Ergebnis der Operation: Die Ressource ist das Video. Die Verarbeitung - das ist, was unter der Haube passiert ist, um das tatsächliche Ergebnis vor der Anforderung und unabhängig von der Anforderung vorzubereiten - ist hier irrelevant. Es kann ein- oder mehrmals aufgerufen werden.
  • GET /video/status/123betrifft die Verarbeitung an sich . Es steht nichts in der Warteschlange. Das resultierende Video ist ihm egal. Die Ressource ist die Verarbeitung selbst. Es kann ein- oder mehrmals aufgerufen werden.
Arseni Mourzenko
quelle
1
Ist eine 202 als Antwort auf eine sinnvoll GET? Das ist sicherlich die richtige Wahl für die Initiale POST, weshalb ich es benutze. Es scheint jedoch semantisch verdächtig GETzu sein, "akzeptiert" zu sagen, wenn es nichts von dieser speziellen Anfrage akzeptiert hat.
Matthew Haugen
2
@MainMa Wie gesagt, ich stelle POSTden Job in die Warteschlange, dann erhalte ich GETdie Ergebnisse, möglicherweise nachdem der Client die Sitzung geschlossen hat. A 404 ist etwas , was ich auch in Betracht gezogen haben, aber es scheint falsch, da der Antrag wird gefunden, es hat einfach nicht abgeschlossen ist. Das würde mir anzeigen, dass der in der Warteschlange stehende Job nicht gefunden wurde, was eine ganz andere Situation ist.
Matthew Haugen
1
@MatthewHaugen: Wenn Sie den GETTeil machen, denken Sie nicht an eine unvollständige Anfrage , sondern an eine Anfrage , um das Ergebnis der Operation zu erhalten . Wenn ich Ihnen zum Beispiel sagen , ein Video zu konvertieren und es dauert fünf Minuten , um es zu tun, für Ihr Interesse an ein umgebautes Video zwei Minuten später sollte in HTTP 404, zur Folge hat, weil das Video ist einfach da noch nicht. Das Anfordern des Fortschritts der Operation selbst wird andererseits wahrscheinlich zu einem HTTP 200 führen, das die Anzahl der konvertierten Bytes, die Geschwindigkeit usw. enthält.
Arseni Mourzenko,
5
Der HTTP-Statuscode für die Ressource, die noch nicht verfügbar ist, schlägt vor, eine 409-Konfliktantwort ("Die Anforderung konnte aufgrund eines Konflikts mit dem aktuellen Status der Ressource nicht abgeschlossen werden.") Anstelle einer 404-Antwort zurückzugeben, falls eine Ressource dies nicht tut existiert nicht, weil es in der Mitte des Erzeugens ist.
Brian
1
@ Brian Ihr Kommentar würde eine vernünftige Antwort auf diese Frage geben. Obwohl ich dann mit "[t] antworten würde, ist sein Code nur in Situationen zulässig, in denen erwartet wird, dass der Benutzer den Konflikt möglicherweise lösen und die Anforderung erneut senden kann", was in meinem Fall nicht unbedingt der Fall ist, aber so scheint es weniger falsch als "nicht gefunden". Ein Teil von mir neigt sich zu einer 409 mit einem angehefteten Retry-After-Header. Das einzige Problem ist, dass es seltsam erscheint, eine 409 für ein GET zurückzugeben, aber ich kann mit dieser Seltsamkeit leben - es ist unwahrscheinlich, dass es in Zukunft anders definiert wird.
Matthew Haugen
5

Die naheliegende Alternative zu all dem - was funktioniert, aber einen Zweck von Statuscodes zunichte macht - wäre, immer die Metadaten einzuschließen:

Dies ist der richtige Weg. Der Status einer Ressource in Bezug auf das domänenspezifische Protokoll (auch als Geschäftslogik bezeichnet) hängt vom Inhaltstyp der Ressourcendarstellung ab.

Hier werden zwei unterschiedliche Konzepte zusammengeführt, die sich tatsächlich unterscheiden. Einer ist der Status der Statusübertragung zwischen Client und Server einer Ressource, und der andere ist der Status der Ressource selbst in dem Kontext, in dem die Geschäftsdomäne die verschiedenen Status dieser Ressource definiert. Letzteres hat nichts mit HTTP-Statuscodes zu tun.

Denken Sie daran, dass die HTTP-Statuscodes der Statusübertragung zwischen Client und Server der Ressource entsprechen, unabhängig von den Details dieser Ressource. Wenn Sie GETals Ressource von Ihrem Client den Server nach einer Darstellung einer Ressource in dem aktuellen Status fragen, in dem sie sich befindet. Dies kann ein Bild eines Vogels, ein Word-Dokument oder die aktuelle Außentemperatur sein. Das HTTP-Protokoll ist egal. Der HTTP-Statuscode entspricht dem Ergebnis dieser Anforderung. Hat der POSTvom Client zum Server übertragen eine Ressource auf den Server, wo der Server gab es dann eine URL, die der Client anzeigen kann? Ja? Dann ist das eine 201 CreatedAntwort.

Die Ressource könnte eine Flugbuchung sein, die sich derzeit im Status "Zu überprüfen" befindet. Es kann sich auch um eine Produktbestellung handeln, die sich im Status "Genehmigt" befindet. Diese Zustände sind domänenspezifisch und nicht das, worum es beim HTTP-Protokoll geht. Das HTTP-Protokoll befasst sich mit der Übertragung von Ressourcen zwischen Client und Server.

Der Punkt von REST und HTTP ist, dass sich die Protokolle nicht mit den Details der Ressourcen befassen. Dies ist absichtlich so, dass es sich nicht um domänenspezifische Probleme handelt, so dass es verwendet werden kann, ohne dass Sie etwas über die domänenspezifischen Probleme wissen müssen. Sie interpretieren nicht neu, was die HTTP-Statuscodes in den verschiedenen Kontexten bedeuten (ein Flugbuchungssystem, ein Bildverarbeitungssystem, ein Videosicherheitssystem usw.).

Das domänenspezifische Material ist für den Client und den Server, um es basierend auf Content Typeder Ressource untereinander herauszufinden . Das HTTP-Protokoll ist dazu nicht in der Lage.

Wie der Client feststellt, dass sich der Status der Anforderungsressource geändert hat, ist die Abfrage die beste Wahl, da sie die Kontrolle über den Client behält und keine ununterbrochene Verbindung voraussetzt. Insbesondere, wenn es möglicherweise Stunden dauern wird, bis sich der Zustand ändert. Selbst wenn Sie mit REST zur Hölle sagen, dass Sie nur die Verbindung offen halten, sie stundenlang offen halten und davon ausgehen, dass nichts schief geht, wäre eine schlechte Idee. Was ist, wenn der Benutzer den Client schließt oder das Netzwerk ausfällt? Wenn die Granularität Stunden beträgt, kann der Client den Status nur alle paar Minuten anfordern, bis sich die Anforderung von "ausstehend" in "erledigt" ändert.

Hoffe das hilft Dinge zu klären

Cormac Mulhall
quelle
"Hat der POST vom Client zum Server eine Ressource zum Server übertragen, wo der Server ihr dann eine URL gegeben hat, die der Client anzeigen kann? Ja? Dann ist das eine Antwort von 201 Created." 202 Accepted ist auch als Antwort darauf akzeptabel, wenn der Server die Ressource nicht sofort verarbeiten kann, wie es das OP tut.
Andy
1
Die Sache ist, dass der Server sofort handelt. Die Ressource wird sofort mit einer URL erstellt. Es ist nur der Status der Ressource "Ausstehend" (oder so). Das ist ein Geschäftsdomänenstaat. In Bezug auf das HTTP-Protokoll handelte der Server, sobald er die Ressource erstellte, und gab dem Client die URL der Ressource. Sie können diese Ressource abrufen. Die POST-Anforderung selbst steht nicht aus. Dies ist, was ich meine, indem ich die beiden verschiedenen konzeptuellen Domänen getrennt halte. Wenn der Client ein Feuer sendet und vergisst, dass die POST-Anforderung stundenlang nicht bearbeitet wurde, ist 202 anwendbar.
Cormac Mulhall
Es interessiert niemanden, ob die URL existiert, aber Sie können die Daten, die die Ressource darstellt, nicht abrufen, da sie noch verarbeitet werden. Könnte auch NICHT die URL erstellen, bis es zum Abrufen des Videos verwendet werden kann.
Andy
Die Ressource wird erstellt, sie befindet sich nur im Status "Ausstehend". Das sind an sich relevante Daten. Irgendwann in der Zukunft kann der Server den Ressourcenstatus auf "Abgeschlossen" (oder "Fehlgeschlagen") ändern, dies ist jedoch ein anderes Konzept als die HTTP-domänenspezifische Aufgabe "Ressource erstellen". Pending kann ein vollkommen gültiger Status für eine "Anforderungs" -Ressource sein, und der Client möchte offensichtlich wissen, dass der Server die Ressource in diesem Status erstellt hat, da er den Server nicht mehr auffordert, die Ressource zu erstellen, um sie abzufragen, um dies herauszufinden wenn sich der Zustand geändert hat.
Cormac Mulhall
4

Ich fand die Vorschläge aus diesem Blog vernünftig: REST und langjährige Jobs .

Zusammenfassen:

  1. Der Server gibt den Code "202 Accepted" zurück, wobei der Header "Location" auf einen URI gesetzt ist, damit der Client den Status überprüft, z. B. "/ queue / 12345".
  2. Bis die Verarbeitung abgeschlossen ist, antwortet der Server auf Statusabfragen mit "200 OK" und einigen Antwortdaten, die den Auftragsstatus anzeigen.
  3. Nach Abschluss der Verarbeitung antwortet der Server auf Statusabfragen mit "303 See Other" und "Location", die URI enthalten, bis zum Endergebnis.
Xiangming Hu
quelle
2

Der HTTP-Statuscode für die noch nicht verfügbare Ressource schlägt vor, eine 409-Konfliktantwort anstelle einer 404-Antwort zurückzugeben, falls eine Ressource nicht vorhanden ist, da sie gerade generiert wird.

Aus der w3-Spezifikation :

10.4.10 409 Konflikt

Die Anforderung konnte aufgrund eines Konflikts mit dem aktuellen Status der Ressource nicht abgeschlossen werden. Dieser Code ist nur in Situationen zulässig, in denen erwartet wird, dass der Benutzer den Konflikt möglicherweise lösen und die Anforderung erneut senden kann. Der Antwortkörper sollte genug enthalten

Informationen, mit denen der Benutzer die Ursache des Konflikts erkennen kann. Im Idealfall würde die Antwortentität genügend Informationen enthalten, damit der Benutzer oder Benutzeragent das Problem beheben kann. Dies ist jedoch möglicherweise nicht möglich und nicht erforderlich.

Konflikte treten am wahrscheinlichsten als Reaktion auf eine PUT-Anforderung auf. Wenn beispielsweise die Versionierung verwendet wird und die Entität, die PUT ist, Änderungen an einer Ressource enthält, die mit den von einer früheren (Drittanbieter-) Anforderung vorgenommenen Konflikten in Konflikt stehen, verwendet der Server möglicherweise die Antwort 409, um anzuzeigen, dass die Anforderung nicht abgeschlossen werden kann . In diesem Fall enthält die Antwortentität wahrscheinlich eine Liste der Unterschiede zwischen den beiden Versionen in einem Format, das durch den Inhaltstyp der Antwort definiert wird.

Dies ist etwas umständlich, da der Code 409 "nur in Situationen zulässig ist, in denen erwartet wird, dass der Benutzer den Konflikt möglicherweise lösen und die Anforderung erneut senden kann". Ich schlage vor, dass der Antworttext eine Nachricht enthält (möglicherweise in einem Antwortformat, das mit dem Rest Ihrer API übereinstimmt), z Versuchen Sie es später noch einmal."

Beachten Sie, dass ich den Ansatz 409 nur empfehlen würde, wenn es sehr wahrscheinlich ist, dass der Benutzer, der die Ressource anfordert, auch der Benutzer ist, der die Generierung dieser Ressource initiiert hat. Benutzer, die nicht an der Generierung der Ressource beteiligt sind, würden einen 404-Fehler weniger verwirrend finden.

Brian
quelle
Scheint eine Strecke zu sein, für die 409 wirklich gedacht ist, was auf einen Put zurückzuführen ist.
Andy
@ Andy: Stimmt, aber so ist jede andere Alternative. Beispiel: 202 ist in Wirklichkeit eine Antwort auf die Anforderung, die die Verarbeitung eingeleitet hat , und nicht auf die Anforderung, die die Ergebnisse der Verarbeitung angefordert hat. In Wirklichkeit ist 404 die Antwort, die den Spezifikationen am besten entspricht, da die Ressource nicht gefunden wurde (weil sie noch nicht vorhanden war). Nichts hindert die API daran, die relevanten API-Daten in der 404-Antwort bereitzustellen. Wohlgemerkt, 4xx / 5xx Antworten sind in der Regel ärgerlich zu konsumieren. Einige Sprachen lösen eine Ausnahme aus, anstatt nur einen anderen Statuscode bereitzustellen.
Brian
2
Nein, insbesondere die letzten Absätze von MainMas Antwort. Separate Endpunkte, um den Status der Anforderung zu überprüfen und das Video selbst abzurufen. Der Status ist nicht dieselbe Ressource wie das Video und sollte für sich allein adressierbar sein.
Andy