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:
- Benutzer füllt Formular aus
- Client sendet Daten an API
- API gibt 202 Accepted zurück
- Der Client leitet den Benutzer zu einer eindeutigen URL für diese Anforderung weiter (
/results/{request_id}
) - ~ Irgendwann ~
- 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 GET
Anfrage, 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) switch
auf , response.data.status
um 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.
POST
Anfrage 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.Antworten:
HTTP 202 akzeptiert (HTTP / 1.1)
Sie suchen nach
HTTP 202 Accepted
Status. Siehe RFC 2616 :HTTP 102-Verarbeitung (WebDAV)
RFC 2518 schlägt vor, Folgendes zu verwenden
HTTP 102 Processing
:aber es hat eine Einschränkung:
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 Accepted
und 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/result
die 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:
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 Accepted
JSON-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:
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 404
da 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:
was zu einer ähnlichen Antwort führen würde:
Wenn Sie die Anforderung immer wieder ausführen, wird der Fortschritt angezeigt, bis Folgendes zutrifft:
Es ist wichtig, einen Unterschied zwischen diesen drei Arten von Anfragen zu machen:
POST /video/convert
Stellt 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/123
betrifft 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/123
betrifft 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.quelle
GET
? Das ist sicherlich die richtige Wahl für die InitialePOST
, weshalb ich es benutze. Es scheint jedoch semantisch verdächtigGET
zu sein, "akzeptiert" zu sagen, wenn es nichts von dieser speziellen Anfrage akzeptiert hat.POST
den Job in die Warteschlange, dann erhalte ichGET
die 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.GET
Teil 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.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
GET
als 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 derPOST
vom 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 eine201 Created
Antwort.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 Type
der 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
quelle
Ich fand die Vorschläge aus diesem Blog vernünftig: REST und langjährige Jobs .
Zusammenfassen:
quelle
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 :
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.
quelle