Grundlegendes zu REST: Verben, Fehlercodes und Authentifizierung


602

Ich suche nach einer Möglichkeit, APIs um Standardfunktionen in meinen PHP-basierten Webanwendungen, Datenbanken und CMS zu wickeln.

Ich habe mich umgesehen und mehrere "Skelett" -Rahmen gefunden. Zusätzlich zu den Antworten in meiner Frage gibt es Tonic , ein REST-Framework, das ich mag, weil es sehr leicht ist.

Ich mag REST am besten wegen seiner Einfachheit und möchte eine darauf basierende API-Architektur erstellen. Ich versuche, mich mit den Grundprinzipien vertraut zu machen und habe sie noch nicht vollständig verstanden. Daher eine Reihe von Fragen.

1. Verstehe ich es richtig?

Angenommen, ich habe eine Ressource "Benutzer". Ich könnte eine Reihe von URIs wie folgt einrichten:

/api/users     when called with GET, lists users
/api/users     when called with POST, creates user record
/api/users/1   when called with GET, shows user record
               when called with PUT, updates user record
               when called with DELETE, deletes user record

Ist dies eine korrekte Darstellung einer RESTful-Architektur?

2. Ich brauche mehr Verben

Das Erstellen, Aktualisieren und Löschen mag theoretisch ausreichen, aber in der Praxis werde ich viel mehr Verben benötigen. Mir ist klar, dass dies Dinge sind, die in eine Aktualisierungsanforderung eingebettet werden könnten , aber es sind bestimmte Aktionen, die bestimmte Rückkehrcodes haben können, und ich möchte sie nicht alle in eine Aktion werfen.

Einige, die im Benutzerbeispiel in den Sinn kommen, sind:

activate_login
deactivate_login
change_password
add_credit

Wie würde ich Aktionen wie in einer RESTful-URL-Architektur ausdrücken?

Mein Instinkt wäre, einen GET-Aufruf an eine URL wie

/api/users/1/activate_login 

und erwarte einen Statuscode zurück.

Dies weicht jedoch von der Idee ab, HTTP-Verben zu verwenden. Was denken Sie?

3. So geben Sie Fehlermeldungen und Codes zurück

Ein großer Teil der Schönheit von REST beruht auf der Verwendung von Standard-HTTP-Methoden. Bei einem Fehler gebe ich einen Header mit einem 3xx-, 4xx- oder 5xx-Fehlerstatuscode aus. Für eine detaillierte Fehlerbeschreibung kann ich den Körper verwenden (richtig?). So weit, ist es gut. Aber wie kann ein proprietärer Fehlercode übertragen werden , der detaillierter beschreibt, was schief gelaufen ist (z. B. "Verbindung zur Datenbank fehlgeschlagen" oder "Datenbankanmeldung falsch")? Wenn ich es zusammen mit der Nachricht in den Körper stecke, muss ich es anschließend analysieren. Gibt es einen Standard-Header für solche Dinge?

4. So führen Sie die Authentifizierung durch

  • Wie würde eine API-Schlüssel-basierte Authentifizierung nach REST-Prinzipien aussehen?
  • Gibt es starke Gründe gegen die Verwendung von Sitzungen bei der Authentifizierung eines REST-Clients, abgesehen davon, dass dies eine offensichtliche Verletzung des REST-Prinzips darstellt? :) (nur ein Scherz hier, sitzungsbasierte Authentifizierung würde gut mit meiner vorhandenen Infrastruktur spielen.)

13
@ Daniel, danke für die Bearbeitung. "Ich mehr Verben" war ein absichtliches Wortspiel, aber ich lasse es so wie es ist, es ist jetzt einfacher zu lesen. :)
Pekka

1
Übrigens zur Fehlerbeschreibung. Am Ende habe ich eine Fehlerbeschreibung in den Header der Antwort eingefügt. Fügen Sie einfach den Header "Fehlerbeschreibung" hinzu.
Andrii Muzychuk

Dies ähnelt eher Fragen zur Anwendungssicherheit. Bei REST geht es nicht um Anwendungssicherheit.
Nazar Merza

@NazarMerza Wie sind 1., 2. und 3. Anwendungssicherheitsfragen?
Pekka

Antworten:


621

Ich habe diese Frage ein paar Tage zu spät bemerkt, aber ich habe das Gefühl, dass ich einen Einblick hinzufügen kann. Ich hoffe, dass dies für Ihr RESTful-Unternehmen hilfreich sein kann.


Punkt 1: Verstehe ich es richtig?

Du hast richtig verstanden. Das ist eine korrekte Darstellung einer RESTful-Architektur. Die folgende Matrix aus Wikipedia ist möglicherweise sehr hilfreich bei der Definition Ihrer Substantive und Verben:


Beim Umgang mit einer Sammlungs- URI wie folgt:http://example.com/resources/

  • GET : Listen Sie die Mitglieder der Sammlung zusammen mit ihren Mitglieds-URIs für die weitere Navigation auf. Listen Sie zum Beispiel alle zum Verkauf stehenden Autos auf.

  • PUT : Bedeutung definiert als "Ersetzen der gesamten Sammlung durch eine andere Sammlung".

  • POST : Erstellen Sie einen neuen Eintrag in der Sammlung, in dem die ID automatisch von der Sammlung zugewiesen wird. Die erstellte ID ist normalerweise Teil der von dieser Operation zurückgegebenen Daten.

  • LÖSCHEN : Bedeutung definiert als "Löschen der gesamten Sammlung".


Im Umgang mit einer Mitglieds- URI wie:http://example.com/resources/7HOU57Y

  • GET : Ruft eine Darstellung des adressierten Mitglieds der Sammlung ab, die in einem geeigneten MIME-Typ ausgedrückt ist.

  • PUT : Aktualisieren Sie das adressierte Mitglied der Sammlung oder erstellen Sie es mit der angegebenen ID.

  • POST : Behandelt das adressierte Mitglied als eigenständige Sammlung und erstellt einen neuen Untergebenen davon.

  • LÖSCHEN : Löscht das adressierte Mitglied der Sammlung.


Punkt 2: Ich brauche mehr Verben

Wenn Sie glauben, dass Sie mehr Verben benötigen, kann dies im Allgemeinen bedeuten, dass Ihre Ressourcen neu identifiziert werden müssen. Denken Sie daran, dass Sie in REST immer auf eine Ressource oder eine Sammlung von Ressourcen reagieren. Was Sie als Ressource auswählen, ist für Ihre API-Definition sehr wichtig.

Anmeldung aktivieren / deaktivieren : Wenn Sie eine neue Sitzung erstellen, möchten Sie möglicherweise "die Sitzung" als Ressource betrachten. Verwenden Sie zum Erstellen einer neuen Sitzung POST to http://example.com/sessions/mit den Anmeldeinformationen im Hauptteil. Verwenden Sie zum Ablaufen PUT oder DELETE (möglicherweise abhängig davon, ob Sie einen Sitzungsverlauf beibehalten möchten) http://example.com/sessions/SESSION_ID.

Passwort ändern: Diesmal ist die Ressource "der Benutzer". Sie benötigen einen PUT http://example.com/users/USER_IDmit den alten und neuen Passwörtern im Textkörper. Sie handeln auf die Ressource "Benutzer", und ein Änderungskennwort ist einfach eine Aktualisierungsanforderung. Es ist der UPDATE-Anweisung in einer relationalen Datenbank ziemlich ähnlich.

Mein Instinkt wäre, einen GET-Aufruf an eine URL wie /api/users/1/activate_login

Dies widerspricht einem sehr zentralen REST-Prinzip: Die korrekte Verwendung von HTTP-Verben. Jede GET-Anfrage sollte niemals Nebenwirkungen haben.

Beispielsweise sollte eine GET-Anforderung niemals eine Sitzung in der Datenbank erstellen, ein Cookie mit einer neuen Sitzungs-ID zurückgeben oder Rückstände auf dem Server hinterlassen. Das GET-Verb ähnelt der SELECT-Anweisung in einem Datenbankmodul. Denken Sie daran, dass die Antwort auf eine Anforderung mit dem GET-Verb bei Anforderung mit denselben Parametern cachefähig sein sollte, genau wie beim Anfordern einer statischen Webseite.


Punkt 3: So geben Sie Fehlermeldungen und Codes zurück

Betrachten Sie die HTTP-Statuscodes 4xx oder 5xx als Fehlerkategorien. Sie können den Fehler im Körper ausarbeiten.

Verbindung zur Datenbank fehlgeschlagen: / Falsche Datenbankanmeldung : Im Allgemeinen sollten Sie für diese Fehlertypen einen 500-Fehler verwenden. Dies ist ein serverseitiger Fehler. Der Kunde hat nichts falsch gemacht. 500 Fehler werden normalerweise als "wiederholbar" betrachtet. Das heißt, der Client kann dieselbe exakte Anforderung erneut versuchen und erwarten, dass sie erfolgreich ist, sobald die Probleme des Servers behoben sind. Geben Sie die Details im Körper an, damit der Kunde uns Menschen einen Kontext bieten kann.

Die andere Kategorie von Fehlern wäre die 4xx-Familie, die im Allgemeinen darauf hinweist, dass der Client etwas falsch gemacht hat. Insbesondere zeigt diese Fehlerkategorie dem Client normalerweise an, dass die Anforderung nicht erneut versucht werden muss, da sie weiterhin dauerhaft fehlschlägt. Das heißt, der Client muss etwas ändern, bevor er diese Anforderung erneut versucht. Beispielsweise würden Fehler "Ressource nicht gefunden" (HTTP 404) oder "Fehlgebildete Anforderung" (HTTP 400) in diese Kategorie fallen.


Punkt 4: So führen Sie eine Authentifizierung durch

Wie in Punkt 1 ausgeführt, möchten Sie möglicherweise anstelle der Authentifizierung eines Benutzers über das Erstellen einer Sitzung nachdenken. Sie erhalten eine neue "Sitzungs-ID" zusammen mit dem entsprechenden HTTP-Statuscode (200: Zugriff gewährt oder 403: Zugriff verweigert).

Sie werden dann Ihren RESTful-Server fragen: "Können Sie mir die Ressource für diese Sitzungs-ID ERHALTEN?".

Es gibt keinen authentifizierten Modus - REST ist zustandslos: Sie erstellen eine Sitzung, fordern den Server auf, Ihnen Ressourcen mit dieser Sitzungs-ID als Parameter bereitzustellen, und beim Abmelden wird die Sitzung gelöscht oder läuft ab.


6
Sehr gut, aber Ihre Verwendung PUTzum Ändern eines Passworts ist wahrscheinlich falsch. PUTerfordert die gesamte Ressource, sodass Sie alle Benutzerattribute senden müssen, um HTTP (und damit HATEOAS REST) ​​zu erfüllen. Um einfach das Passwort zu ändern, sollte man PATCHoder verwenden POST.
Lawrence Dol

1
Ich denke, dieser Beitrag wäre perfekt, wenn Sie näher darauf eingehen würden, was "POST: Behandelt das angesprochene Mitglied als eigenständige Sammlung und erstellt einen neuen Untergebenen davon". meint. - Ich habe herausgefunden, was es mit Googeln bedeutet - es ist eine Ausnahme von Ihrer ansonsten großartigen Antwort.
Martin Konecny

6
Ich stimme dem allerletzten Satz nicht zu. Sie erklären, wie zustandslos REST ist. Das Anmelden zum Erstellen einer Sitzung und das anschließende Abmelden zum Beenden der Sitzung nach einigen Arbeiten ist das beste Beispiel für eine statusbehaftete API.
Brandon

1
"Dies widerspricht einem sehr zentralen REST-Prinzip: Die korrekte Verwendung von HTTP-Verben. Jede GET-Anfrage sollte niemals Nebenwirkungen hinterlassen." - Was ist, wenn Sie eine Trefferanzahl für die Ressource beibehalten möchten?
Bobbyalex

1
Dieser Artikel sollte Ihre Fragen beantworten. saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices
java_geek

79

Einfach gesagt, Sie machen das völlig rückwärts.

Sie sollten sich dem nicht über die URLs nähern, die Sie verwenden sollten. Die URLs werden effektiv "kostenlos" bereitgestellt, sobald Sie entschieden haben, welche Ressourcen für Ihr System erforderlich sind UND wie Sie diese Ressourcen darstellen und welche Wechselwirkungen zwischen den Ressourcen und dem Anwendungsstatus bestehen.

Um Roy Fielding zu zitieren

Eine REST-API sollte fast den gesamten beschreibenden Aufwand für die Definition der Medientypen verwenden, die zur Darstellung von Ressourcen und zur Steuerung des Anwendungsstatus verwendet werden, oder für die Definition erweiterter Beziehungsnamen und / oder hypertextfähiger Markierungen für vorhandene Standardmedientypen. Jeder Aufwand, der aufgewendet wird, um zu beschreiben, welche Methoden für welche URIs von Interesse verwendet werden sollen, sollte vollständig im Rahmen der Verarbeitungsregeln für einen Medientyp definiert werden (und in den meisten Fällen bereits durch vorhandene Medientypen definiert). [Ein Fehler hier impliziert, dass Out-of-Band-Informationen die Interaktion anstelle von Hypertext steuern.]

Die Leute beginnen immer mit den URIs und denken, dass dies die Lösung ist, und dann neigen sie dazu, ein Schlüsselkonzept in der REST-Architektur zu übersehen, insbesondere, wie oben zitiert: "Ein Fehler hier impliziert, dass Out-of-Band-Informationen die Interaktion anstelle von Hypertext steuern. ""

Um ehrlich zu sein, sehen viele eine Reihe von URIs und einige GETs und PUTs und POSTs und denken, dass REST einfach ist. REST ist nicht einfach. RPC über HTTP ist einfach, das Verschieben von Datenblobs, die über HTTP-Nutzdaten übertragen werden, ist einfach. REST geht jedoch darüber hinaus. REST ist protokollunabhängig. HTTP ist nur sehr beliebt und für REST-Systeme geeignet.

REST lebt von den Medientypen, ihren Definitionen und davon, wie die Anwendung die Aktionen steuert, die diesen Ressourcen über Hypertext (Links, effektiv) zur Verfügung stehen.

Es gibt unterschiedliche Ansichten zu Medientypen in REST-Systemen. Einige bevorzugen anwendungsspezifische Nutzdaten, während andere vorhandene Medientypen in Rollen heben, die für die Anwendung geeignet sind. Zum einen haben Sie einerseits bestimmte XML-Schemata, die für Ihre Anwendung geeignet sind, anstatt XHTML als Repräsentation zu verwenden, möglicherweise über Mikroformate und andere Mechanismen.

Ich denke, beide Ansätze haben ihren Platz. Das XHTML funktioniert sehr gut in Szenarien, die sowohl das vom Menschen als auch von der Maschine gesteuerte Web überlappen, während die früheren, spezifischeren Datentypen meiner Meinung nach die Interaktion von Maschine zu Maschine besser erleichtern. Ich finde, dass die Hebung von Warenformaten die Verhandlung von Inhalten möglicherweise schwierig machen kann. "application / xml + yourresource" ist als Medientyp viel spezifischer als "application / xhtml + xml", da letzteres für viele Nutzdaten gelten kann, an denen ein Maschinenclient möglicherweise tatsächlich interessiert ist oder nicht ohne Selbstbeobachtung bestimmen.

XHTML funktioniert jedoch (offensichtlich) sehr gut im menschlichen Web, wo Webbrowser und Rendering sehr wichtig sind.

Ihre Bewerbung wird Sie bei solchen Entscheidungen unterstützen.

Ein Teil des Entwurfs eines REST-Systems besteht darin, die erstklassigen Ressourcen in Ihrem System zusammen mit den abgeleiteten Unterstützungsressourcen zu ermitteln, die zur Unterstützung der Vorgänge auf den Primärressourcen erforderlich sind. Sobald die Ressourcen erkannt wurden, werden die Darstellung dieser Ressourcen sowie die Zustandsdiagramme den Ressourcenfluss über Hypertext innerhalb der Darstellungen als nächste Herausforderung darstellen.

Denken Sie daran, dass jede Darstellung einer Ressource in einem Hypertextsystem sowohl die tatsächliche Ressourcendarstellung als auch die für die Ressource verfügbaren Statusübergänge kombiniert. Betrachten Sie jede Ressource als Knoten in einem Diagramm, wobei die Verknüpfungen die Linien sind, die diesen Knoten zu anderen Zuständen führen. Diese Links informieren Kunden nicht nur darüber, was getan werden kann, sondern auch darüber, was für sie erforderlich ist (da ein guter Link den URI und den erforderlichen Medientyp kombiniert).

Zum Beispiel können Sie haben:

<link href="http://example.com/users" rel="users" type="application/xml+usercollection"/>
<link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>

Ihre Dokumentation enthält Informationen zum rel-Feld "Benutzer" und zum Medientyp "application / xml + youruser".

Diese Links scheinen redundant zu sein, sie sprechen alle ziemlich genau mit derselben URI. Aber sie sind es nicht.

Dies liegt daran, dass in diesem Link für die Beziehung "Benutzer" die Sammlung von Benutzern behandelt wird und Sie die einheitliche Oberfläche verwenden können, um mit der Sammlung zu arbeiten (GET, um alle abzurufen, DELETE, um alle zu löschen usw.).

Wenn Sie an diese URL POSTEN, müssen Sie ein "application / xml + usercollection" -Dokument übergeben, das wahrscheinlich nur eine einzelne Benutzerinstanz im Dokument enthält, damit Sie den Benutzer hinzufügen können oder nicht, um möglicherweise mehrere um hinzuzufügen Einmal. Möglicherweise schlägt Ihre Dokumentation vor, dass Sie anstelle der Sammlung einfach einen einzelnen Benutzertyp übergeben können.

Sie können sehen, was die Anwendung benötigt, um eine Suche durchzuführen, wie durch den Link "Suchen" und dessen Medientyp definiert. In der Dokumentation zum Suchmedientyp erfahren Sie, wie sich dies verhält und was als Ergebnis zu erwarten ist.

Das Wichtigste dabei ist jedoch, dass die URIs selbst im Grunde genommen unwichtig sind. Die Anwendung kontrolliert die URIs, nicht die Clients. Abgesehen von einigen „Einstiegspunkten“ sollten sich Ihre Kunden bei ihrer Arbeit auf die von der Anwendung bereitgestellten URIs verlassen.

Der Kunde muss wissen, wie er die Medientypen manipuliert und interpretiert, muss sich aber nicht darum kümmern, wohin sie gehen.

Diese beiden Links sind in den Augen eines Kunden semantisch identisch:

<link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>
<link href="http://example.com/AW163FH87SGV" rel="search" type="application/xml+usersearchcriteria"/>

Konzentrieren Sie sich also auf Ihre Ressourcen. Konzentrieren Sie sich auf ihre Zustandsübergänge in der Anwendung und darauf, wie dies am besten erreicht wird.


1
Danke Will für diese sehr tiefe Antwort. Mehrere Punkte genommen. Mir ist klar, dass die Planung von "wie die URL aussieht" umgekehrt ist, und ich plane auch von der Ressourcenseite. Wenn ich URLs zum Spielen habe, kann ich das Konzept leichter verstehen. Es kann sein, dass meine Anforderungen mit einem System erfüllt werden können, das nicht zu 100% den REST-Prinzipien entspricht, wie Sie sie hier definieren. Ich werde eine vollständige Liste der Anforderungen für jeden Ressourcentyp erstellen. Ich denke, ich kann mich dann entscheiden. Prost.
Pekka

30

Zu 1 : Das sieht soweit gut aus. Denken Sie daran, den URI des neu erstellten Benutzers in einem "Location:" - Header als Teil der Antwort auf POST zusammen mit einem Statuscode "201 Created" zurückzugeben.

zu 2: Die Aktivierung über GET ist eine schlechte Idee, und das Einfügen des Verbs in die URI ist ein Designgeruch. Möglicherweise möchten Sie ein Formular in einem GET zurückgeben. In einer Web-App wäre dies ein HTML-Formular mit einer Senden-Schaltfläche. Im API-Anwendungsfall möchten Sie möglicherweise eine Darstellung, die einen URI enthält, an PUT zurückgeben, um das Konto zu aktivieren. Natürlich können Sie diesen URI auch in die Antwort auf POST an / users aufnehmen. Durch die Verwendung von PUT wird sichergestellt, dass Ihre Anfrage idempotent ist, dh sie kann sicher erneut gesendet werden, wenn der Client nicht sicher ist, ob sie erfolgreich ist. Überlegen Sie sich im Allgemeinen, in welche Ressourcen Sie Ihre Verben verwandeln können (eine Art "Nomenifizierung von Verben"). Fragen Sie sich, auf welche Methode Ihre spezifische Aktion am besten abgestimmt ist. ZB change_password -> PUT; deaktivieren -> wahrscheinlich LÖSCHEN; add_credit -> möglicherweise POST oder PUT.

zu 3. Erfinden Sie keine neuen Statuscodes, es sei denn, Sie glauben, dass sie so allgemein sind, dass sie eine globale Standardisierung verdienen. Versuchen Sie, den am besten geeigneten verfügbaren Statuscode zu verwenden (lesen Sie alle Informationen in RFC 2616). Fügen Sie zusätzliche Informationen in den Antworttext ein. Wenn Sie wirklich, wirklich sicher sind, dass Sie einen neuen Statuscode erfinden möchten, denken Sie noch einmal darüber nach. Wenn Sie dies immer noch glauben, stellen Sie sicher, dass Sie mindestens die richtige Kategorie auswählen (1xx -> OK, 2xx -> informativ, 3xx -> Umleitung; 4xx-> Clientfehler, 5xx -> Serverfehler). Habe ich erwähnt, dass es eine schlechte Idee ist, neue Statuscodes zu erfinden?

Zu 4. Verwenden Sie nach Möglichkeit das in HTTP integrierte Authentifizierungsframework. Überprüfen Sie, wie Google die Authentifizierung in GData durchführt. Fügen Sie im Allgemeinen keine API-Schlüssel in Ihre URIs ein. Vermeiden Sie Sitzungen, um die Skalierbarkeit zu verbessern und das Caching zu unterstützen. Wenn die Antwort auf eine Anforderung aufgrund eines zuvor aufgetretenen Ereignisses unterschiedlich ist, haben Sie sich normalerweise an eine bestimmte Serverprozessinstanz gebunden. Es ist viel besser, den Sitzungsstatus entweder in einen Clientstatus zu verwandeln (z. B. als Teil nachfolgender Anforderungen) oder ihn explizit zu machen, indem Sie ihn in den (Server-) Ressourcenstatus verwandeln, dh ihm einen eigenen URI geben.


Können Sie diskutieren, warum API-Schlüssel nicht in URLs eingefügt werden sollen? Liegt es daran, dass sie in Proxy-Protokollen sichtbar sind? Was ist, wenn die Tasten vorübergehend und zeitbasiert sind? Was ist, wenn HTTPS verwendet wird?
MikeSchinkel

4
Abgesehen von der Verletzung des Geistes (URIs sollten Dinge identifizieren) ist die Hauptfolge, dass es das Caching ruiniert.
Stefan Tilkov

22

1. Sie haben die richtige Idee, wie Sie Ihre Ressourcen gestalten sollen, IMHO. Ich würde nichts ändern.

2. Anstatt zu versuchen, HTTP mit mehr Verben zu erweitern, sollten Sie überlegen, auf was Ihre vorgeschlagenen Verben im Hinblick auf die grundlegenden HTTP-Methoden und -Ressourcen reduziert werden können. Anstelle eines activate_loginVerbs können Sie beispielsweise Ressourcen wie den folgenden einrichten: /api/users/1/login/activeDies ist ein einfacher Boolescher Wert. Um ein Login zu aktivieren, muss nur PUTein Dokument mit der Aufschrift "true" oder 1 oder was auch immer angezeigt werden. Zum Deaktivieren PUTein Dokument, das leer ist oder 0 oder falsch sagt.

Um Passwörter zu ändern oder festzulegen, gehen Sie einfach PUTzu /api/users/1/password.

Wann immer Sie etwas hinzufügen müssen (wie eine Gutschrift), denken Sie in POSTs. Sie können beispielsweise POSTeine Ressource wie /api/users/1/creditseinen Körper bearbeiten, der die Anzahl der hinzuzufügenden Credits enthält. Ein PUTauf derselben Ressource könnte verwendet werden, um den Wert zu überschreiben, anstatt ihn hinzuzufügen. Ein POSTmit einer negativen Zahl im Körper würde subtrahieren und so weiter.

3. Ich würde dringend davon abraten, die grundlegenden HTTP-Statuscodes zu erweitern. Wenn Sie keine finden können, die genau zu Ihrer Situation passt, wählen Sie die nächstgelegene aus und fügen Sie die Fehlerdetails in den Antworttext ein. Denken Sie auch daran, dass HTTP-Header erweiterbar sind. Ihre Anwendung kann alle benutzerdefinierten Header definieren, die Ihnen gefallen. Eine Anwendung, an der ich beispielsweise gearbeitet habe, kann 404 Not Foundunter mehreren Umständen eine zurückgeben. Anstatt den Client dazu zu bringen, den Antworttext aus diesem Grund zu analysieren, haben wir nur einen neuen Header hinzugefügt X-Status-Extended, der unsere proprietären Statuscode-Erweiterungen enthielt. Möglicherweise sehen Sie eine Antwort wie:

HTTP/1.1 404 Not Found    
X-Status-Extended: 404.3 More Specific Error Here

Auf diese Weise weiß ein HTTP-Client wie ein Webbrowser immer noch, was mit dem regulären 404-Code zu tun ist, und ein komplexerer HTTP-Client kann den X-Status-ExtendedHeader nach genaueren Informationen durchsuchen.

4. Für die Authentifizierung empfehle ich die Verwendung der HTTP-Authentifizierung, wenn Sie können. Aber meiner Meinung nach ist die Verwendung der Cookie-basierten Authentifizierung nichts Falsches, wenn dies für Sie einfacher ist.


4
Gute Idee, "erweiterte" Ressourcen zu verwenden, um kleinere Teile einer größeren Ressource zu bearbeiten.
Womble

1
Cookies sind in HTTP / REST gültig, aber der Server sollte das Cookie nicht als Status speichern (also nicht als Sitzung). Der Cookie kann jedoch einen Wert wie einen HMAC speichern, der zerlegt werden kann, ohne den Status an anderer Stelle nachzuschlagen.
Bruce Alderson

14

REST-Grundlagen

REST hat eine einheitliche Schnittstellenbeschränkung, die besagt, dass sich der REST-Client auf Standards anstatt auf anwendungsspezifische Details des tatsächlichen REST-Service verlassen muss, damit der REST-Client nicht durch geringfügige Änderungen beschädigt wird und wahrscheinlich wiederverwendbar ist.

Es besteht also ein Vertrag zwischen dem REST-Client und dem REST-Service. Wenn Sie HTTP als zugrunde liegendes Protokoll verwenden, sind die folgenden Standards Bestandteil des Vertrags:

  • HTTP 1.1
    • Methodendefinitionen
    • Statuscode-Definitionen
    • Cache-Steuerungsheader
    • Accept- und Content-Type-Header
    • Auth-Header
  • IRI (utf8 URI )
  • Körper (wählen Sie eine)
  • Hyperlinks
    • was sollte sie enthalten (wählen Sie eine)
      • Senden von Link-Headern
      • Senden einer Hypermedia-Antwort, z. B. HTML, Atom + XML, Hal + JSON, LD + JSON & Hydra usw.
    • Semantik
      • Verwenden Sie IANA-Link-Relationen und wahrscheinlich benutzerdefinierte Link-Relationen
      • Verwenden Sie ein anwendungsspezifisches RDF-Vokabular

REST hat eine zustandslose Einschränkung, die erklärt, dass die Kommunikation zwischen dem REST-Service und dem Client zustandslos sein muss. Dies bedeutet, dass der REST-Service die Client-Status nicht verwalten kann, sodass Sie keinen serverseitigen Sitzungsspeicher haben können. Sie müssen jede einzelne Anfrage authentifizieren. So ist beispielsweise die HTTP-Basisauthentifizierung (Teil des HTTP-Standards) in Ordnung, da bei jeder Anforderung der Benutzername und das Kennwort gesendet werden.

Um Ihnen Fragen zu beantworten

  1. Ja das kann sein.

    Um nur zu erwähnen, die Clients interessieren sich nicht für die IRI-Struktur, sie interessieren sich für die Semantik, weil sie Links mit Link-Relationen oder RDF-Attributen (Linked Data) folgen.

    Das einzig Wichtige an den IRIs ist, dass ein einzelnes IRI nur eine einzelne Ressource identifizieren darf. Es ist einer einzelnen Ressource wie einem Benutzer gestattet, viele verschiedene IRIs zu haben.

    Es ist ziemlich einfach, warum wir nette IRIs wie verwenden /users/123/password; Es ist viel einfacher, die Routing-Logik auf den Server zu schreiben, wenn Sie die IRI einfach durch Lesen verstehen.

  2. Sie haben mehr Verben wie PUT, PATCH, OPTIONS und noch mehr, aber Sie brauchen nicht mehr davon ... Anstatt neue Verben hinzuzufügen, müssen Sie lernen, wie Sie neue Ressourcen hinzufügen.

    activate_login -> PUT /login/active true deactivate_login -> PUT /login/active false change_password -> PUT /user/xy/password "newpass" add_credit -> POST /credit/raise {details: {}}

    (Die Anmeldung ist aus REST-Sicht aufgrund der zustandslosen Einschränkung nicht sinnvoll.)

  3. Ihren Benutzern ist es egal, warum das Problem besteht. Sie möchten nur wissen, ob es Erfolg oder Fehler gibt, und wahrscheinlich eine Fehlermeldung, die sie verstehen können, zum Beispiel: "Entschuldigung, aber wir konnten Ihren Beitrag nicht speichern." Usw.

    Die HTTP-Status-Header sind Ihre Standard-Header. Alles andere sollte im Körper sein, denke ich. Ein einziger Header reicht nicht aus, um beispielsweise detaillierte mehrsprachige Fehlermeldungen zu beschreiben.

  4. Die zustandslose Einschränkung (zusammen mit den Einschränkungen für den Cache und das geschichtete System) stellt sicher, dass der Dienst gut skaliert werden kann. Sie werden sicherlich nicht Millionen von Sitzungen auf dem Server verwalten wollen, wenn Sie dasselbe auf den Clients tun können ...

    Der Client eines Drittanbieters erhält ein Zugriffstoken, wenn der Benutzer über den Hauptclient Zugriff darauf gewährt. Danach sendet der Drittanbieter-Client das Zugriffstoken bei jeder Anforderung. Es gibt kompliziertere Lösungen, zum Beispiel können Sie jede einzelne Anfrage unterschreiben usw. Weitere Einzelheiten finden Sie im OAuth-Handbuch.

Ähnliche Literatur


11

Für die von Ihnen angegebenen Beispiele würde ich Folgendes verwenden:

enable_login

POST /users/1/activation

disable_login

DELETE /users/1/activation

Passwort ändern

PUT /passwords (Dies setzt voraus, dass der Benutzer authentifiziert ist.)

Guthaben hinzufügen

POST /credits (Dies setzt voraus, dass der Benutzer authentifiziert ist.)

Bei Fehlern geben Sie den Fehler im Textkörper in dem Format zurück, in dem Sie die Anforderung erhalten haben. Wenn Sie also Folgendes erhalten:

DELETE /users/1.xml

Sie würden die Antwort in XML zurücksenden, dasselbe gilt für JSON usw.

Für die Authentifizierung sollten Sie die http-Authentifizierung verwenden.


1
Ich würde es nicht createals Teil des URI verwenden (denken Sie daran, dass URIs Substantive sein sollten und HTTP-Methoden Verben sein sollten, die mit diesen Substantiven arbeiten.) Stattdessen hätte ich eine Ressource wie /users/1/activediese, die ein einfacher Boolescher Wert sein kann und es auch sein kann Wird durch Setzen einer 1 oder 0 auf diese Ressource festgelegt.
Friedo

Du hast recht, ich habe das / create herausgenommen. Es sollte nur ein Beitrag zur Singleton-Ressource sein.
Jonnii

3
Ich würde es auch nicht activationfür den URI verwenden, es sei denn, Sie manipulieren und verwalten eine Ressource explizit unter dem Namen /users/1/activation. Was macht ein GET dazu? Was macht ein PUT? Ich habe das Gefühl, dass Sie die URI verbalisieren. Auch bei der Aushandlung von Inhaltstypen wird dies häufig am besten aus dem URI herausgelassen und in Header eingefügt, wie z Accept.
Cheeso

6
  1. Verwenden Sie post, wenn Sie nicht wissen, wie der neue Ressourcen-URI aussehen würde (Sie erstellen einen neuen Benutzer, die Anwendung weist dem neuen Benutzer seine ID zu), PUT, um Ressourcen zu aktualisieren oder zu erstellen, von denen Sie wissen, wie sie dargestellt werden sollen (Beispiel) : PUT /myfiles/thisismynewfile.txt)
  2. Geben Sie die Fehlerbeschreibung im Nachrichtentext zurück
  3. Sie können die HTTP-Authentifizierung verwenden (wenn dies ausreicht). Webdienste sollten stateles sein

5

Ich würde vorschlagen (als ersten Durchgang), dass PUTnur zum Aktualisieren vorhandener Entitäten verwendet werden sollte. POSTsollte zum Erstellen neuer verwendet werden. dh

/api/users     when called with PUT, creates user record

fühlt sich für mich nicht richtig an. Der Rest Ihres ersten Abschnitts (Re-Verb-Verwendung) sieht jedoch logisch aus.


wahrscheinlich dachte jemand, dass dies nicht wirklich eine Antwort auf seine Frage war
lubos hasko

6
Meine Einstellung zu PUT im Vergleich zu POST zum Erstellen neuer Entitäten besteht darin, PUT zu verwenden, wenn der Aufrufer den Ressourcennamen steuert, sodass Sie die genaue Ressource und POST verwenden können, wenn der Angerufene den neuen Ressourcennamen steuert (wie im Beispiel hier).
SteveD

5

Ausführlich, aber aus der HTTP 1.1-Methodenspezifikation unter http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html kopiert

9.3 GET

Die GET-Methode bedeutet, dass alle Informationen (in Form einer Entität) abgerufen werden, die durch den Request-URI identifiziert werden. Wenn sich der Anforderungs-URI auf einen Datenerzeugungsprozess bezieht, sind es die erzeugten Daten, die als Entität in der Antwort und nicht als Quelltext des Prozesses zurückgegeben werden sollen, es sei denn, dieser Text ist zufällig die Ausgabe des Prozesses.

Die Semantik der GET-Methode ändert sich in ein "bedingtes GET", wenn die Anforderungsnachricht ein Headerfeld "If-Modified-Since", "If-Unmodified-Since", "If-Match", "If-None-Match" oder "If-Range" enthält. Eine bedingte GET-Methode fordert an, dass die Entität nur unter den in den bedingten Headerfeldern beschriebenen Umständen übertragen wird. Die bedingte GET-Methode soll die unnötige Netzwerknutzung reduzieren, indem zwischengespeicherte Entitäten aktualisiert werden können, ohne dass mehrere Anforderungen erforderlich sind oder bereits vom Client gehaltene Daten übertragen werden.

Die Semantik der GET-Methode ändert sich in ein "partielles GET", wenn die Anforderungsnachricht ein Bereichskopffeld enthält. Ein Teil-GET fordert, dass nur ein Teil der Entität übertragen wird, wie in Abschnitt 14.35 beschrieben. Die partielle GET-Methode soll die unnötige Netzwerknutzung reduzieren, indem teilweise abgerufene Entitäten abgeschlossen werden können, ohne dass bereits vom Client gehaltene Daten übertragen werden.

Die Antwort auf eine GET-Anforderung kann nur dann zwischengespeichert werden, wenn sie die in Abschnitt 13 beschriebenen Anforderungen für das HTTP-Caching erfüllt.

In Abschnitt 15.1.3 finden Sie Sicherheitsaspekte bei Verwendung für Formulare.

9.5 POST

Die POST-Methode wird verwendet, um anzufordern, dass der Ursprungsserver die in der Anforderung enthaltene Entität als neuen Untergebenen der Ressource akzeptiert, die durch den Anforderungs-URI in der Anforderungszeile identifiziert wird. POST wurde entwickelt, um eine einheitliche Methode zu ermöglichen, die die folgenden Funktionen abdeckt:

  - Annotation of existing resources;
  - Posting a message to a bulletin board, newsgroup, mailing list,
    or similar group of articles;
  - Providing a block of data, such as the result of submitting a
    form, to a data-handling process;
  - Extending a database through an append operation.

Die tatsächliche Funktion der POST-Methode wird vom Server festgelegt und hängt normalerweise vom Request-URI ab. Die veröffentlichte Entität ist dieser URI auf dieselbe Weise untergeordnet, wie eine Datei einem Verzeichnis untergeordnet ist, das sie enthält, ein Nachrichtenartikel einer Newsgroup untergeordnet ist, in der sie veröffentlicht wurde, oder ein Datensatz einer Datenbank untergeordnet ist.

Die von der POST-Methode ausgeführte Aktion führt möglicherweise nicht zu einer Ressource, die durch einen URI identifiziert werden kann. In diesem Fall ist entweder 200 (OK) oder 204 (kein Inhalt) der geeignete Antwortstatus, je nachdem, ob die Antwort eine Entität enthält, die das Ergebnis beschreibt oder nicht.

Wenn eine Ressource auf dem Ursprungsserver erstellt wurde, MUSS die Antwort 201 (Erstellt) sein und eine Entität enthalten, die den Status der Anforderung beschreibt und auf die neue Ressource verweist, sowie einen Standortheader (siehe Abschnitt 14.30).

Antworten auf diese Methode können nicht zwischengespeichert werden, es sei denn, die Antwort enthält die entsprechenden Cache-Control- oder Expires-Headerfelder. Die Antwort 303 (siehe Andere) kann jedoch verwendet werden, um den Benutzeragenten anzuweisen, eine zwischengespeicherte Ressource abzurufen.

POST-Anforderungen MÜSSEN die in Abschnitt 8.2 festgelegten Anforderungen für die Nachrichtenübertragung erfüllen.

Siehe Abschnitt 15.1.3 für Sicherheitsüberlegungen.

9.6 PUT

Die PUT-Methode fordert an, dass die eingeschlossene Entität unter dem angegebenen Anforderungs-URI gespeichert wird. Wenn sich der Anforderungs-URI auf eine bereits vorhandene Ressource bezieht, MUSS die eingeschlossene Entität als modifizierte Version der auf dem Ursprungsserver befindlichen Entität betrachtet werden. Wenn der Anforderungs-URI nicht auf eine vorhandene Ressource verweist und dieser URI vom anfordernden Benutzeragenten als neue Ressource definiert werden kann, kann der Ursprungsserver die Ressource mit diesem URI erstellen. Wenn eine neue Ressource erstellt wird, MUSS der Ursprungsserver den Benutzeragenten über die Antwort 201 (Erstellt) informieren. Wenn eine vorhandene Ressource geändert wird, MÜSSEN entweder die Antwortcodes 200 (OK) oder 204 (Kein Inhalt) gesendet werden, um den erfolgreichen Abschluss der Anforderung anzuzeigen. Wenn die Ressource nicht mit dem Request-URI erstellt oder geändert werden konnte, Es sollte eine angemessene Fehlerantwort gegeben werden, die die Art des Problems widerspiegelt. Der Empfänger der Entität DARF KEINE Content- * (z. B. Content-Range) -Header ignorieren, die er nicht versteht oder implementiert, und MUSS in solchen Fällen eine 501-Antwort (nicht implementiert) zurückgeben.

Wenn die Anforderung einen Cache durchläuft und der Anforderungs-URI eine oder mehrere aktuell zwischengespeicherte Entitäten identifiziert, MÜSSEN diese Einträge als veraltet behandelt werden. Antworten auf diese Methode können nicht zwischengespeichert werden.

Der grundlegende Unterschied zwischen den POST- und PUT-Anforderungen spiegelt sich in der unterschiedlichen Bedeutung des Anforderungs-URI wider. Der URI in einer POST-Anforderung gibt die Ressource an, die die eingeschlossene Entität verarbeitet. Diese Ressource kann ein Datenakzeptanzprozess, ein Gateway zu einem anderen Protokoll oder eine separate Entität sein, die Anmerkungen akzeptiert. Im Gegensatz dazu identifiziert der URI in einer PUT-Anforderung die der Anforderung beigefügte Entität. Der Benutzeragent weiß, welcher URI beabsichtigt ist, und der Server darf NICHT versuchen, die Anforderung auf eine andere Ressource anzuwenden. Wenn der Server möchte, dass die Anforderung auf einen anderen URI angewendet wird,

es MUSS eine 301-Antwort (dauerhaft verschoben) senden; Der Benutzeragent kann dann selbst entscheiden, ob die Anforderung umgeleitet werden soll oder nicht.

Eine einzelne Ressource kann durch viele verschiedene URIs identifiziert werden. Beispielsweise kann ein Artikel einen URI zum Identifizieren "der aktuellen Version" haben, der von dem URI getrennt ist, der jede bestimmte Version identifiziert. In diesem Fall kann eine PUT-Anforderung für einen allgemeinen URI dazu führen, dass mehrere andere URIs vom Ursprungsserver definiert werden.

HTTP / 1.1 definiert nicht, wie sich eine PUT-Methode auf den Status eines Ursprungsservers auswirkt.

PUT-Anforderungen MÜSSEN die in Abschnitt 8.2 festgelegten Anforderungen für die Nachrichtenübertragung erfüllen.

Sofern für einen bestimmten Entitätsheader nicht anders angegeben, MÜSSEN die Entitätsheader in der PUT-Anforderung auf die vom PUT erstellte oder geänderte Ressource angewendet werden.

9.7 LÖSCHEN

Die DELETE-Methode fordert den Ursprungsserver auf, die durch den Request-URI identifizierte Ressource zu löschen. Diese Methode kann durch menschliches Eingreifen (oder auf andere Weise) auf dem Ursprungsserver überschrieben werden. Dem Client kann nicht garantiert werden, dass der Vorgang ausgeführt wurde, selbst wenn der vom Ursprungsserver zurückgegebene Statuscode anzeigt, dass die Aktion erfolgreich abgeschlossen wurde. Der Server sollte jedoch KEINEN Erfolg anzeigen, es sei denn, er beabsichtigt zum Zeitpunkt der Antwort, die Ressource zu löschen oder an einen unzugänglichen Ort zu verschieben.

Eine erfolgreiche Antwort sollte 200 (OK) sein, wenn die Antwort eine Entität enthält, die den Status beschreibt, 202 (Akzeptiert), wenn die Aktion noch nicht ausgeführt wurde, oder 204 (Kein Inhalt), wenn die Aktion ausgeführt wurde, die Antwort jedoch nicht enthält eine Entität.

Wenn die Anforderung einen Cache durchläuft und der Anforderungs-URI eine oder mehrere aktuell zwischengespeicherte Entitäten identifiziert, MÜSSEN diese Einträge als veraltet behandelt werden. Antworten auf diese Methode können nicht zwischengespeichert werden.


2

Informationen zu REST-Rückkehrcodes: Es ist falsch , HTTP-Protokollcodes und REST-Ergebnisse zu mischen.

Ich habe jedoch viele Implementierungen gesehen, die sie gemischt haben, und viele Entwickler stimmen mir möglicherweise nicht zu.

HTTP-Rückkehrcodes beziehen sich auf sich HTTP Requestselbst. Ein REST-Aufruf wird mithilfe einer Hypertext Transfer Protocol-Anforderung ausgeführt und arbeitet auf einer niedrigeren Ebene als die aufgerufene REST-Methode selbst. REST ist ein Konzept / Ansatz, und seine Ausgabe ist ein geschäftliches / logisches Ergebnis, während HTTP-Ergebniscode ein Transport ist .

Die Rückgabe von "404 Not found" beim Aufrufen von / users / ist beispielsweise verwirrend, da dies Folgendes bedeuten kann:

  • URI ist falsch (HTTP)
  • Es wurden keine Benutzer gefunden (REST)

"403 Verboten / Zugriff verweigert" kann bedeuten:

  • Besondere Erlaubnis erforderlich. Browser können damit umgehen, indem sie den Benutzer / das Passwort fragen. (HTTP)
  • Auf dem Server konfigurierte falsche Zugriffsberechtigungen. (HTTP)
  • Sie müssen authentifiziert sein (REST)

Und die Liste kann mit '500 Server error' (ein Apache / Nginx HTTP-Fehler oder ein Business Constraint-Fehler in REST) ​​oder anderen HTTP-Fehlern usw. fortgesetzt werden.

Aus dem Code ist schwer zu verstehen, was der Fehlergrund, ein HTTP-Fehler (Transportfehler) oder ein REST-Fehler (logisch) war.

Wenn die HTTP-Anforderung physisch erfolgreich ausgeführt wurde, sollte immer 200 Code zurückgegeben werden, unabhängig davon, ob die Datensätze gefunden wurden oder nicht. Weil die URI-Ressource gefunden wurde und vom http-Server verarbeitet wurde. Ja, möglicherweise wird ein leerer Satz zurückgegeben. Ist es möglich, eine leere Webseite mit 200 als http-Ergebnis zu erhalten?

Stattdessen können Sie 200 HTTP-Code und einfach einen JSON mit einem leeren Array / Objekt zurückgeben oder ein Bool-Ergebnis- / Erfolgsflag verwenden, um über den Status der ausgeführten Operation zu informieren.

Einige Internetanbieter können Ihre Anfragen abfangen und Ihnen einen 404-http-Code zurückgeben. Dies bedeutet nicht, dass Ihre Daten nicht gefunden werden, aber auf Transportebene stimmt etwas nicht.

Aus dem Wiki :

Im Juli 2004 setzte der britische Telekommunikationsanbieter BT Group das Cleanfeed-System zum Blockieren von Inhalten ein, das bei jeder Anforderung von Inhalten, die von der Internet Watch Foundation als potenziell illegal eingestuft wurden, einen 404-Fehler zurückgibt. Andere ISPs geben unter den gleichen Umständen einen "verbotenen" HTTP 403-Fehler zurück. In Thailand und Tunesien wurde auch über die Praxis berichtet, gefälschte 404-Fehler als Mittel zur Verschleierung der Zensur einzusetzen. In Tunesien, wo die Zensur vor der Revolution von 2011 streng war, wurden die Menschen auf die Natur der gefälschten 404-Fehler aufmerksam und schufen einen imaginären Charakter namens "Ammar 404", der "den unsichtbaren Zensor" darstellt.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.