Sollte ich HTTP-Statuscodes verwenden, um Ereignisse auf Anwendungsebene zu beschreiben?

54

Einige Server, mit denen ich mich befasst habe, geben HTTP 200 für Anforderungen zurück, bei denen der Client einen Fehler in Betracht ziehen sollte, mit so etwas wie "Erfolg: Falsch" im Körper.

Dies scheint mir keine ordnungsgemäße Implementierung von HTTP-Codes zu sein, insbesondere bei fehlgeschlagener Authentifizierung. Ich habe HTTP-Fehlercodes kurz und bündig gelesen, da "4xx" angibt, dass die Anforderung erst nach einer Änderung erneut ausgeführt werden soll, während "5xx" angibt, dass die Anforderung möglicherweise gültig ist oder nicht und erneut versucht werden kann, aber erfolglos war. In diesem Fall 200: Anmeldung fehlgeschlagen, oder 200: Datei konnte nicht gefunden werden, oder 200: Parameter x fehlt, scheint definitiv falsch zu sein.

Andererseits konnte ich das Argument sehen, dass "4xx" nur ein strukturelles Problem mit der Anfrage anzeigen sollte. Das ist also richtig, um 200 zurückzugeben: Falscher Benutzer / falsches Kennwort anstatt 401 nicht autorisiert, da der Client die Anfrage stellen darf, sie aber falsch ist. Dieses Argument lässt sich so zusammenfassen, dass der Antwortcode 200 betragen sollte, wenn der Server die Anforderung verarbeiten und überhaupt eine Entscheidung treffen konnte, und der Client den Body auf weitere Informationen überprüfen muss.

Grundsätzlich scheint dies eine Frage der Präferenz zu sein. Aber das ist unbefriedigend. Wenn also jemand einen Grund hat, warum eines dieser Paradigmen korrekter ist, würde ich gerne wissen.

Kagan Mattson
quelle
9
success: falseimpliziert, dass die Anfrage fehlgeschlagen ist und Sie es wissen. Das sollte eine 500 sein. So etwas wie Ihr falscher Benutzername / Passwort wäre eine 401. Das ist nicht so vieldeutig.
Pete
4
Dies ist eine dieser Fragen, die meiner Meinung nach Religionskriege hervorrufen können. Bei einer RESTful-API ist die Antwort klar, aber es gibt andere Arten von APIs, bei denen HTTP nur als Transportschicht behandelt wird. In diesen Fällen sollten Anwendungsfehler nicht in diese Schicht übergehen.
Gort the Robot
5
Wenn ich mir nicht sicher bin, welchen http-Status ich zurückgeben soll, ist es immer verlockend, 418 "Ich bin eine Teekanne" zu wählen.
Joshp
1
Ein Beispiel sind mehrere (gestapelte) Anforderungen und Antworten . Batching ist keine erholsame Sache; Bedenken hinsichtlich der praktischen Effizienz erfordern jedoch häufig eine gewisse Unterstützung für das Batching über Bedenken hinsichtlich der Eleganz.
Rwong

Antworten:

35

Interessante Frage.

Grundsätzlich können wir dies auf den richtigen Weg reduzieren, um Dinge analog zu OSI-Schichten zu klassifizieren. HTTP wird üblicherweise als Protokoll auf Anwendungsebene definiert, und HTTP ist in der Tat ein generisches Client / Server-Protokoll.

In der Praxis ist der Server jedoch fast immer ein Weiterleitungsgerät, und der Client ist ein Webbrowser, der für das Interpretieren und Rendern von Inhalten verantwortlich ist: Der Server leitet die Dinge nur an eine beliebige Anwendung weiter, und diese Anwendungen senden beliebige Skripts zurück, die der Browser sendet ist verantwortlich für die Ausführung. Die HTTP-Interaktion selbst - die Anfrage- / Antwortformulare, Statuscodes usw. - ist hauptsächlich eine Frage der Frage, wie beliebige Inhalte so effizient wie möglich angefordert, bereitgestellt und wiedergegeben werden können, ohne sich in die Quere zu kommen. Viele der Statuscodes und Header sind in der Tat für diese Zwecke konzipiert.

Das Problem beim Versuch, das HTTP-Protokoll für die Verarbeitung anwendungsspezifischer Flows zu huckepacken, besteht darin, dass Sie nur eine von zwei Optionen haben: 1) Sie müssen Ihre Anforderungs- / Antwortlogik zu einer Teilmenge der HTTP-Regeln machen. oder 2) Sie müssen bestimmte Regeln wiederverwenden, und dann wird die Trennung von Bedenken in der Regel unscharf. Das kann auf den ersten Blick schön und sauber aussehen, aber ich denke, es ist eine dieser Designentscheidungen, die Sie am Ende bereuen, wenn sich Ihr Projekt weiterentwickelt.

Daher würde ich sagen, dass es besser ist, explizit über die Trennung von Protokollen zu sprechen. Lassen Sie den HTTP-Server und den Webbrowser ihr eigenes Ding machen und lassen Sie die App ihr eigenes Ding machen. Die App muss in der Lage sein, Anforderungen zu stellen, und sie muss die Antworten bereitstellen. Die Logik zum Anfordern und Interpretieren der Antworten kann komplexer (oder weniger komplexer) sein als die HTTP-Perspektive.

Der andere erwähnenswerte Vorteil dieses Ansatzes besteht darin, dass Anwendungen im Allgemeinen nicht von einem zugrunde liegenden Transportprotokoll abhängig sein sollten (aus logischer Sicht). HTTP selbst hat sich in der Vergangenheit geändert, und jetzt haben wir HTTP 2, das SPDY folgt. Wenn Sie Ihre App als nicht mehr als ein HTTP-Funktions-Plug-in betrachten, können Sie dort hängen bleiben, wenn neue Infrastrukturen übernommen werden.

Yam Marcovic
quelle
8
Sehr aufschlussreich. Das stärkste Argument hier ist die (Impedanz-) Nichtübereinstimmung zwischen HTTP-Statuscodes und den Rückgabewerten Ihrer App. Dies kann auf lange Sicht zu einem Albtraum werden. Darüber hinaus unterstütze ich die Trennung der Bedenken zwischen Transport (HTTP) und Nutzlast (App-Daten). Wenn Sie die URL eines Service-Endpunkts falsch eingeben, wird eine 404 angezeigt. Wenn Sie den Service nach einem nicht vorhandenen Element fragen, wird eine app-spezifische Nachricht angezeigt (möglicherweise mit zusätzlichen Informationen, die Sie zur Behebung des Problems verwenden können).
2
Wenn Sie die URL falsch eingeben, landen Sie möglicherweise nicht einmal auf dem richtigen Server, und dann kann alles Mögliche passieren.
gnasher729
Dies ist ein schön nuancierter Look. Ich denke, das Problem, dass HTTP zu einer Pseudotransportschicht wird, ist das eigentliche Problem bei der Bestimmung. Diese Frage tritt am häufigsten auf, wenn Sie einen Nginx- oder Apache-Server als Proxy für einen NodeJS-Server haben, auf dem der Proxy bereits Regeln zum Senden dieser Codes enthält, und die Frage lautet, ob das Backend dem Standard entsprechen soll. In einigen dieser Fälle kann es einen Konstruktionsgrund geben, keinen Fehlercode zu senden, da nginx diesen möglicherweise als "Backend-Down" interpretiert.
Kagan Mattson
4
Genau. Es ist nichts Falsches daran, dass ein Anwendungsschichtfehler in einer HTTP 200-Antwort gemeldet wird. Der 200 zeigt an, dass die HTTP-Anforderung / Antwort selbst erfolgreich war, ohne etwas über ihren Inhalt oder die Semantik der Anwendungsschicht zu sagen, die zu diesem Zeitpunkt aufgerufen wird.
Leichtigkeit Rennen mit Monica
22

Diese Frage ist ein bisschen meinungsbasiert, aber so oder so.

So wie ich es sehe, können 200 "weiche Fehler" auslösen. Beim Erstellen von APIs versuche ich, zwischen diesen und "harten Fehlern" zu unterscheiden.

"Soft Errors" werden mit einem Statuscode von 200 bedient, enthalten jedoch eine Fehlerbeschreibung und den Erfolgsstatus von false. "Weiche Fehler" treten nur auf, wenn das Ergebnis "wie erwartet" ist, aber nicht im engeren Sinne ein Erfolg.

Es ist wichtig zu beachten, dass "weiche Fehler" eher ein Hinweis für den Implementierer sind. Aus diesem Grund ist es wichtig, weitere Informationen zu dem Fehler bereitzustellen, z. B. eine von Menschen lesbare Fehlermeldung und / oder eine Art Code, mit dem der Endbenutzer eine Rückmeldung erhalten kann. Diese Fehler liefern dem Implementierer (und dem Endbenutzer) weitere Informationen darüber, was auf der Serverseite passiert ist .

Angenommen, Sie haben eine API mit einer Suchfunktion, aber während einer Suche werden keine Ergebnisse ausgegeben. Dies ist nicht falsch, aber es ist auch kein "Erfolg", nicht im engsten Sinne der Definition.

Beispiel formatiert als JSON:

{
    "meta" {
        "success": false,
        "message": "Search yielded no results",
        "code": "NORESULTS"
    }
    "data": []
}

"Harte Fehler" hingegen werden mit einem Statuscode bedient, der für den Fehler empfohlen wird. Benutzer nicht eingeloggt? - 403 / 401. Falsche Eingabe? - 400. Serverfehler? - 50X. Und so weiter.

Auch hier ist es ein bisschen meinungsbasiert. Manche Leute wollen alle Fehler gleich behandeln, "harte Fehler" alles. Keine Suchergebnisse? Das ist ein 404! Auf der anderen Seite der Medaille, keine Suchergebnisse? - Das ist wie erwartet kein Fehler.

Ein weiterer wichtiger Faktor, den Sie berücksichtigen sollten, ist beispielsweise Ihre Architektur. wenn Sie mit Ihrer API mithilfe von JavaScript XHR-Anforderungen und jQuery oder AngularJS interagieren. Diese "harten Fehler" müssen mit einem separaten Rückruf behandelt werden, wohingegen die "weichen Fehler" mit dem "Erfolg" -Rückruf behandelt werden können. Nichts zu brechen, das Ergebnis ist immer noch "wie erwartet". Der clientseitige Code kann dann den Erfolgsstatus und den Code (oder die Nachricht) anzeigen. Und drucken Sie das an den Endbenutzer.

die maus
quelle
2
Eigentlich ist es eine merkwürdige Entscheidung, dies als Fehler auf API-Ebene zu klassifizieren. Auch wenn der Kunde es nach eigenem Ermessen als unerwartet auf Benutzerebene einstufen kann.
Deduplizierer
1
Es gibt viele Dinge, die berücksichtigt werden müssen. Alles hängt von der Implementierung der API ab. Es ist auch wieder ein bisschen meinungsbasiert und hängt auch davon ab, was die API als "Erfolg" und / oder "Fehler" definiert. Die "success": false-Flag ist eher ein Hinweis für den Implementierer, dass etwas los ist . Normalerweise sollte es mit einem internen Statuscode gehen. Entweder "code": "NORESULTS"oder ein numerischer Code - was auch immer der Ersteller der API vorhat. Es ist meistens dort, damit jeder, der die API implementiert, Informationen darüber ableiten kann, was auf dem Server passiert ist.
Die Maus
15

Es gibt zwei Aspekte einer API: den Aufwand, die API zu implementieren, und den Aufwand aller Clients, die API ordnungsgemäß zu verwenden.

Als Autor des Clients weiß ich, dass ich beim Senden einer Anfrage an einen Webserver möglicherweise eine Fehlermeldung (nie richtig mit dem Server gesprochen) oder eine Antwort mit einem Statuscode erhalte. Ich muss mit den Fehlern umgehen. Ich muss mit einer guten Antwort umgehen. Ich muss mit erwarteten, dokumentierten, "schlechten" Antworten umgehen. Ich muss damit umgehen, was sonst noch zurückkommt.

Beim Entwerfen der API sollten Sie sich ansehen, was für den Client am einfachsten zu verarbeiten ist. Wenn der Client eine wohlgeformte Anfrage sendet und Sie das tun können, worum Sie gebeten werden, sollten Sie eine Antwort im Bereich 200 geben (in einigen Fällen ist eine andere Zahl als 200 in diesem Bereich angemessen).

Wenn der Client fragt "gib mir alle Datensätze wie ..." und es gibt null, dann ist eine 200 mit Erfolg und einem Array von null Datensätzen völlig angemessen. Die Fälle, die Sie erwähnen:

"Anmeldung fehlgeschlagen" sollte normalerweise eine Zahl von 401 sein. "Datei konnte nicht gefunden werden" sollte eine Zahl von 404 sein. "Fehlender Parameter x" sollte eine Zahl von 500 sein (tatsächlich eine Zahl von 400, wenn der Server feststellt, dass die Anforderung fehlerhaft ist, und 500 wenn der Server durch meine Anfrage total verwirrt ist und keine Ahnung hat, was los ist). In diesen Fällen ist die Rückgabe von 200 sinnlos. Es bedeutet nur, dass ich als Autor eines Kunden nicht nur den Statuscode einsehen kann, sondern auch die Antwort studieren muss. Ich kann nicht einfach sagen "Status 200, großartig, hier sind die Daten".

Vor allem der "Parameter fehlt" - damit würde ich nie fertig werden . Es bedeutet, dass meine Anfrage falsch ist. Wenn meine Anfrage falsch ist, kann ich diese falsche Anfrage nicht rückgängig machen. Ich würde zunächst eine korrekte Anfrage senden. Jetzt bin ich gezwungen damit umzugehen. Ich bekomme eine 200 und muss prüfen, ob eine Antwort "parameter missing" vorliegt. Das ist schrecklich.

Am Ende gibt es ein Dutzend oder zwei Statuscodes für die Behandlung vieler verschiedener Situationen, und Sie sollten sie verwenden.

gnasher729
quelle
3
Wenn ich mich mit einer API verbinde, würde ich persönlich lieber 200 für eine 'Datei nicht gefunden' erhalten, wenn ich mich mit einem gültigen Endpunkt verbinde, da dann meine HTTP-Behandlung nicht in die Ebene übergehen muss, die die API darüber verwaltet.
Whatsisname
4
"Fehlender Parameter x" sollte 400 BAD_REQUEST sein, da der Client etwas falsch macht. 500 INTERNAL_SERVER_ERROR sollte für Fälle reserviert werden, in denen der Server das Falsche tut. Ein 500 bedeutet, dass der Client es möglicherweise erneut versuchen kann. Eine 400 impliziert, dass jemand den Client reparieren sollte.
Gort the Robot
1
Wenn Sie eine RESTful-Schnittstelle schreiben, identifiziert die URL ein bestimmtes Objekt und daher ist ein 404 angemessen. Dies /customers/premium/johndoe.jsonbezieht sich konzeptionell auf einen Kunden, der sich nicht in der Datenbank befindet, und /files/morefiles/customers.htmlauf eine Seite, die sich nicht im Dateisystem befindet.
Gort the Robot
@whatsisname Was Sie sagen, ist sinnvoll, da dann nicht klar ist, ob der Endpunkt fehlerhaft ist oder die Ressource nicht vorhanden ist. Sie können auch argumentieren, dass an dieser Adresse keine Ressource vorhanden ist, unabhängig davon, ob der Endpunkt gültig ist oder nicht, sodass a 404in beiden Fällen korrekt ist.
Pete
2
Eine Sache, die ich nicht erwähnt habe, ist, dass Sie möglicherweise Informationen verlieren, wenn Sie Anwendungsfehler auf HTTP-Statuscodes setzen. Wenn die App nur ein 404 und nichts anderes zurückgibt, wissen Sie nicht, ob es daran lag, dass Ihre API ein 404 zurückgab oder der Server die Datei nicht finden konnte. Das könnte Ihrem Debugging einen zusätzlichen Schritt hinzufügen.
AmadeusDrZaius