it-swarm.com.de

Rufen Sie eine serverseitige Methode für eine Ressource auf eine REST-gerechte Weise auf

Denken Sie daran, dass ich ein grundlegendes Verständnis von REST habe. Angenommen, ich habe diese URL:

http://api.animals.com/v1/dogs/1/

Und jetzt möchte ich den Kellner dazu bringen, dass der Hund bellt. Nur der Server weiß, wie das geht. Nehmen wir an, ich möchte, dass es bei einem CRON-Job ausgeführt wird, bei dem der Hund für den Rest der Ewigkeit alle 10 Minuten bellt. Wie sieht dieser Aufruf aus? Ich möchte das irgendwie machen:

URL-Anfrage:

ACTION http://api.animals.com/v1/dogs/1/

Im Anfragetext:

{"action":"bark"}

Bevor Sie sich über meine eigene HTTP-Methode ärgern, helfen Sie mir und geben Sie mir eine bessere Vorstellung davon, wie ich eine serverseitige Methode auf REST-artige Weise aufrufen sollte. :)

BEARBEITEN ZUR ERKLÄRUNG

Weitere Erläuterungen zur Funktionsweise der "Rinden" -Methode. Hier sind einige Optionen, die zu unterschiedlich strukturierten API-Aufrufen führen können:

  1. bark schickt einfach eine E-Mail an dog.email und nimmt nichts auf.
  2. bark sendet eine E-Mail an dog.email und erhöht dog.barkCount um 1.
  3. bark erstellt mit bark.timestamp eine neue "Rinden" -Aufzeichnung, wenn die Rinde auftrat. Außerdem wird dog.barkCount um 1 erhöht.
  4. bark führt einen Systembefehl aus, um die neueste Version des Hundecodes von Github abzurufen. Anschließend wird eine SMS an dog.owner gesendet, in der angegeben wird, dass der neue Hundecode in Produktion ist.
132
Kirk Ouimet

Warum ein RESTful Design anstreben?

Die RESTful-Prinzipienbringen die Funktionen, die Websites einfach machen(für einen zufälligen menschlichen Benutzer "surfen")zum API-Design der Webdienste, daher sind sie für einen Programmierer einfach zu bedienen. REST ist nicht gut, weil es REST ist, es ist gut, weil es gut ist. Und es ist gut, hauptsächlich, weil eseinfachist.

Die Einfachheit von einfachem HTTP (ohne SOAP Envelopes und Single-URI überladene POST Dienste), wiemanche nennen können "Mangel an Funktionen", ist eigentlichseine größte Stärke. Auf Anhieb bittet HTTP Sie um Adressierbarkeit ​​und Staatenlosigkeit =: Die beiden grundlegenden Entwurfsentscheidungen, mit denen HTTP auf die heutigen Megasites (und Megaservices) skalierbar bleibt.

Aber REST ist nicht das Beste:Manchmal ist ein RPC-ähnlicher("Remote Procedure Call" - wie SOAP)angemessenund manchmal haben andere Bedürfnisse Vorrang vor den Tugenden des Webs. Das ist in Ordnung. Was wir nicht wirklich mögenist unnötige Komplexität. Zu oft bringt ein Programmierer oder eine Firma In RPC-ähnlichen Diensten für einen Auftrag, den einfaches altes HTTP problemlos verarbeiten kann: Der Effekt ist, dass HTTP auf ein Transportprotokoll für eine enorme XML-Nutzlast reduziert wird, die erklärt, was "wirklich" vor sich geht (nicht der URI oder die HTTP-Methode) eine Ahnung davon) .Der resultierende Service ist viel zu komplex, unmöglich zu debuggen und funktioniert nur, wenn Ihre Kunden das vom Entwickler beabsichtigtegenaue Setuphaben.

Ebenso wie ein Java/C # -Code nicht ​​objektorientiert sein kann, macht nur die Verwendung von HTTP ein Design nicht RESTful. Man kann in der Hektik vonDenkenüber seine Dienstein Bezug auf Aktionen und Remote-Methoden, die aufgerufen werden sollten, gefangen sein. Kein Wunder, dass dies meist in einem RPC-Style-Service (oder einem REST-RPC-Hybrid) enden wird. Der erste Schritt ist, anders zu denken. Ein RESTful-Design kann auf viele Arten erreicht werden. Eine Möglichkeit (die einfachste, könnte man sagen) besteht darin,Ihre Anwendung in Bezug auf Ressourcen und nicht auf Aktionen zu betrachten:

  • Anstatt in Handlungen zu denken ("Suche nach Orten auf der Karte"),
  • Denken Sie an die Ergebnisse dieser Aktion ("die Liste der Orte auf der Karte, die einem Suchkriterium entsprechen").

Ich werde für Beispiele unten gehen. (Ein weiterer wichtiger Aspekt von REST ist die Verwendung von HATEOAS - ich putze es hier nicht, aber ich spreche schnell darüber in einem anderen Beitrag .)

Über den ersten Entwurf

Werfen wir einen Blick auf das vorgeschlagene Design:

ACTION http://api.animals.com/v1/dogs/1/

Zunächst sollten wir nicht erwägen, einneues HTTP-Verb(ACTION) zu erstellen. Im Allgemeinen ist dies nerwünscht ​​aus mehreren Gründen:

  • (1)Woher weiß ein "zufälliger" Programmierer, dass das Verb ACTION existiert, wenn nur die Dienst-URI angegeben wird?
  • (2)Wenn der Programmierer weiß, dass es existiert, wie wird er seine Semantik kennen? Was bedeutet das Verb?
  • (3)welche eigenschaften (sicherheit, idempotenz) sollte man von diesem verb erwarten?
  • (4)was ist, wenn der Programmierer einen sehr einfachen Client hat, der nur Standard-HTTP-Verben verarbeitet?
  • (5)...

Lassen Sie uns nundie Verwendung von POSTin Betracht ziehen (ich werde im Folgenden erläutern, warum, nimm einfach mein Wort dafür):

POST /v1/dogs/1/ HTTP/1.1
Host: api.animals.com

{"action":"bark"}

Dies könnte OK sein ... abernur wenn:

  • {"action":"bark"} War ein Dokument; und
  • /v1/dogs/1/ War ein "Document Processor" (Factory-ähnlicher) URI. Ein "Dokumentenprozessor" ist eine URI, auf die Sie nur "werfen" und die Sie "vergessen" - der Prozessor leitet Sie möglicherweise nach dem "Werfen" zu einer neu erstellten Ressource weiter. Z.B. Der URI zum Posten von Nachrichten bei einem Nachrichtenbroker-Service, der Sie nach dem Posten zu einem URI weiterleitet, der den Status der Nachrichtenverarbeitung anzeigt.

Ich weiß nicht viel über Ihr System, aber ich wette, beide sind nicht wahr:

  • {"action":"bark"}ist kein Dokument, es ist tatsächlichdie Methode, die Sie versuchen, Ninja-Sneak in die Bedienung; und
  • der URI /v1/dogs/1/ stellt eine "Hund" -Ressource dar (wahrscheinlich der Hund mit id==1) und keinen Dokumentprozessor.

Alles was wir jetzt wissen ist, dass das obige Design nicht so RUHIG ist, aber was ist das genau?Was ist daran so schlimm?Grundsätzlich ist es schlimm, weil es sich um eine komplexe URI mit komplexen Bedeutungen handelt. Daraus kann man nichts schließen. Wie würde ein Programmierer wissen, dass ein Hund eine bark Aktion hat, die heimlich mit einer POST infundiert werden kann?

Entwerfen der API-Aufrufe Ihrer Frage

Also lasst uns auf den Punkt kommen und versuchen, diese Rinden REST-konform zu gestalten, indem wirin Bezug auf Ressourcendenken. Gestatten Sie mir, das Buch Restful Web Services zu zitieren:

Eine POST -Anforderung ist ein Versuch, eine neue Ressource aus einer vorhandenen zu erstellen. Die vorhandene Ressource kann im Sinne der Datenstruktur die übergeordnete Ressource der neuen Ressource sein, so wie die Wurzel eines Baums die übergeordnete Ressource aller seiner Blattknoten ist. Oder die vorhandene Ressource kann eine spezielle "Fabrik" Ressource sein, deren einziger Zweck darin besteht, andere Ressourcen zu generieren. Die zusammen mit einer POST Anfrage gesendete Darstellung beschreibt den Anfangszustand der neuen Ressource. Wie bei PUT muss eine POST -Anforderung überhaupt keine Repräsentation enthalten.

Anhand der obigen Beschreibung können wir sehen, dassbarkalseine Subressource einer dog(seit ...) modelliert werden kann Eine bark ist in einem Hund enthalten, das heißt, eine Rinde wird "gebellt" (voneinem Hund).

Aus dieser Überlegung haben wir bereits:

  • Die Methode ist POST
  • Die Ressource ist /barks, Unterressource des Hundes: /v1/dogs/1/barks Und repräsentiert eine bark "Fabrik". Dieser URI ist für jeden Hund eindeutig (da er unter /v1/dogs/{id} Steht).

Jetzt hat jeder Fall Ihrer Liste ein bestimmtes Verhalten.

1. bark sendet nur eine E-Mail an dog.email Und zeichnet nichts auf.

Ist das Bellen (Senden einer E-Mail) eine synchrone oder eine asynchrone Aufgabe? Zweitens erfordert die Anforderung bark ein Dokument (die E-Mail, vielleicht) oder ist es leer?


1.1 bark sendet eine E-Mail an dog.email Und zeichnet nichts auf (als synchrone Aufgabe)

Dieser Fall ist einfach. Ein Aufruf der Factory-Ressource barks ergibt sofort ein Bellen (eine gesendete E-Mail) und die Antwort (ob OK oder nicht) wird sofort gegeben:

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

(entity-body is empty - or, if you require a **document**, place it here)

200 OK

Da es nichts aufzeichnet (ändert), ist 200 OK Genug. Es zeigt, dass alles wie erwartet gelaufen ist.


1.2 bark sendet eine E-Mail an dog.email Und zeichnet nichts auf (als asynchrone Aufgabe)

In diesem Fall muss der Client eine Möglichkeit haben, die Task bark zu verfolgen. Die Task bark sollte dann eine Ressource mit einer eigenen URI sein:

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

{document body, if needed;
NOTE: when possible, the response SHOULD contain a short hypertext note with a hyperlink
to the newly created resource (bark) URI, the same returned in the Location header
(also notice that, for the 202 status code, the Location header meaning is not
standardized, thus the importance of a hipertext/hyperlink response)}

202 Accepted
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

Auf diese Weise ist jede bark nachvollziehbar. Der Client kann dann eine GET an die bark-URI ausgeben, um den aktuellen Status zu ermitteln. Verwenden Sie möglicherweise sogar eine DELETE, um sie abzubrechen.


2. bark sendet eine E-Mail an dog.email Und erhöht dann dog.barkCount Um 1

Dies kann schwieriger sein, wenn Sie dem Client mitteilen möchten, dass die Ressource dog geändert wird:

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

{document body, if needed; when possible, containing a hipertext/hyperlink with the address
in the Location header -- says the standard}

303 See Other
Location: http://api.animals.com/v1/dogs/1

In diesem Fall soll der Header der location dem Client mitteilen, dass er sich dog ansehen soll. Aus dem HTTP RFC über 303 :

Diese Methode gibt es hauptsächlich, um die Ausgabe einesPOST- aktivierten Skripts zu ermöglichen, um den Benutzeragenten zu einer ausgewählten Ressource umzuleiten.

Wenn die Aufgabe asynchron ist, wird eine bark -Unterressource benötigt, genau wie in der Situation 1.2, Und die Angabe 303 Sollte bei Ausführung der Aufgabe mit einem GET .../barks/Y Zurückgegeben werden Komplett.


3. bark erstellt einen neuen Datensatz "bark" mit der Aufzeichnung bark.timestamp, Als die Barke auftrat. Außerdem wird dog.barkCount Um 1 erhöht.

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

(document body, if needed)

201 Created
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

In diesem Fall wird die bark aufgrund der Anforderung erstellt, sodass der Status 201 Created Angewendet wird.

Wenn die Erstellung asynchron ist, ist stattdessen ein 202 Accepted Erforderlich ( wie im HTTP-RFC angegeben ).

Der gespeicherte Zeitstempel ist Teil der Ressource bark und kann mit einer GET abgerufen werden. Der aktualisierte Hund kann auch in diesem GET dogs/X/barks/Y "Dokumentiert" werden.


4. bark führt einen Systembefehl aus, um die neueste Version des Hundecodes von Github abzurufen. Anschließend wird eine SMS an dog.owner Gesendet, in der darauf hingewiesen wird, dass der neue Hundecode in Produktion ist.

Der Wortlaut dieses Artikels ist kompliziert, aber es ist eine einfache asynchrone Aufgabe:

POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=

(document body, if needed)

202 Accepted
Location: http://api.animals.com/v1/dogs/1/barks/a65h44

Der Client gab dann GETs an /v1/dogs/1/barks/a65h44 Aus, um den aktuellen Status zu erfahren (wenn der Code abgerufen wurde, wurde die E-Mail an den Eigentümer gesendet und so weiter). Immer wenn sich der Hund ändert, gilt ein 303.


Einpacken

Zitat Roy Fielding :

Das einzige, was REST von Methoden verlangt, ist, dass sie für alle Ressourcen einheitlich definiert sind (dh, dass Vermittler den Ressourcentyp nicht kennen müssen, um die Bedeutung der Anforderung zu verstehen) .

In den obigen Beispielen ist POST einheitlich gestaltet. Es wird der Hund "bark" machen. Das ist weder sicher (das heißt, Rinde hat Auswirkungen auf die Ressourcen) noch idempotent (jede Anforderung ergibt eine neue bark), was gut zum Verb POST passt.

Ein Programmierer würde wissen: eine POST zu barks ergibt eine bark. Die Antwortstatuscodes (ggf. auch mit Entity-Body und Headern) erläutern, was sich geändert hat und wie der Client vorgehen kann und soll.

Hinweis: Die primären Quellen waren: " Restful Web Services ", das HTTP RFC und Roy Fieldings Blog .




Bearbeiten:

Die Frage und damit die Antwort haben sich seit ihrer Entstehung stark verändert. Dieursprüngliche Fragezum Design einer URI lautete wie folgt:

ACTION http://api.animals.com/v1/dogs/1/?action=bark

Nachfolgend finden Sie eine Erklärung, warum dies keine gute Wahl ist:

Wie Clients dem Server mitteilenWAS ZU TUN ISTmit den Daten ist die Methodeninformation.

  • RESTful-Webdienste übermitteln Methodeninformationen in der HTTP-Methode.
  • Typische Dienste im RPC-Stil und SOAP behalten ihre im Entity-Body- und im HTTP-Header.

WELCHER TEILder Daten [der Client möchte, dass der Server] verarbeitet, ist Bereichsinformationen.

  • RESTful-Services verwenden den URI. SOAP/RPC-Dienste verwenden wieder die Entity-Body- und HTTP-Header.

Nehmen Sie als Beispiel Googles URI http://www.google.com/search?q=DOG. Dort lautet die Methodeninformation GET und die Scoping-Information /search?q=DOG.

Um es kurz zu machen:

  • InRESTful-Architekturengehen die Methodeninformationen in die HTTP-Methode.
  • InRessourcenorientierte Architekturenwerden die Informationen zum Gültigkeitsbereich in den URI übernommen.

Und die Faustregel:

Wenn die HTTP-Methode nicht mit den Methodeninformationen übereinstimmt, ist der Service nicht RESTful. Wenn die Informationen zum Gültigkeitsbereich nicht in der URI enthalten sind, ist der Service nicht ressourcenorientiert.

Sie können "bark""action" in die URL (oder in den Entity-Body) einfügen und POST verwenden. Kein Problem, es funktioniert und ist vielleicht der einfachste Weg,aber das ist nicht RESTful.

Um Ihren Service wirklich RESTful zu halten, müssen Sie möglicherweise einen Schritt zurücktreten und darüber nachdenken, was Sie hier wirklich tun möchten (welche Auswirkungen hat dies auf die Ressourcen).

Ich kann nicht über Ihre spezifischen Geschäftsanforderungen sprechen, aber ich möchte Ihnen ein Beispiel geben: Stellen Sie sich einen REST-fähigen Bestellservice vor, bei dem Bestellungen unter URIs wie example.com/order/123 Erfolgen.

Sagen wir jetzt, wir möchten eine Bestellung stornieren. Wie machen wir das? Man könnte in Versuchung geraten zu denken, dass dies eine "Stornierung""Aktion" ist, und dies als POST example.com/order/123?do=cancel Zu entwerfen.

Das ist nicht RESTful, wie wir oben gesprochen haben. Stattdessen könnten wir PUT eine neue Darstellung der order mit einem canceled -Element an true senden:

PUT /order/123 HTTP/1.1
Content-Type: application/xml

<order id="123">
    <customer id="89987">...</customer>
    <canceled>true</canceled>
    ...
</order>

Und das ist es. Wenn die Bestellung nicht storniert werden kann, kann ein bestimmter Statuscode zurückgegeben werden. (Ein Subressourcendesign wie POST /order/123/canceled Mit dem Entity-Body true kann der Einfachheit halber auch verfügbar sein.)

In Ihrem speziellen Szenario können Sie etwas Ähnliches ausprobieren. Auf diese Weise könnte beispielsweise während ein Hund bellt, eine GET bei /v1/dogs/1/ Diese Information enthalten (z. B. <barking>true</barking>). Oder ... wenn das zu kompliziert ist, lösen Sie Ihre REST-Anforderung und bleiben Sie bei POST.

Aktualisieren:

Ich möchte die Antwort nicht zu groß machen, aber es dauert eine Weile, bis ein Algorithmus (eine Aktion) als eine Menge von Ressourcen verfügbar gemacht wird. Anstatt in Aktionen zu denken ("Suche nach Orten auf der Karte"), muss man in den Ergebnissen dieser Aktion nachdenken ("die Liste der Orte auf der Karte") die Karte entspricht einem Suchkriterium ").

Möglicherweise kehren Sie zu diesem Schritt zurück, wenn Sie feststellen, dass Ihr Entwurf nicht zur einheitlichen HTTP-Schnittstelle passt.

AbfragevariablensindBereichsinformationen, geben abernichtneue Ressourcen an (/post?lang=en Ist eindeutig dasgleicheRessource wie /post?lang=jp, nur eine andere Darstellung). Sie werden vielmehr verwendet, umclient state(wie ?page=10) Zu übermitteln, damit dieser Status nicht auf dem Server gespeichert wird. ?lang=en Ist auch hier ein BeispielEingabeparameterbis algorithmische Ressourcen (/search?q=dogs, /dogs?code=1). Auch hier nicht unterschiedliche Ressourcen.

Eigenschaften der HTTP-Verben (Methoden):

Ein weiterer klarer Punkt, der ?action=something In der URI anzeigt, ist nicht RESTful, sondern die Eigenschaften von HTTP-Verben:

  • GET und HEAD sind sicher (und idempotent);
  • PUT und DELETE sind nur idempotent;
  • POST ist keine.

Sicherheit: Eine GET oder HEAD Anfrage ist eine Anfrage nachlesen/ einige Daten, keine Aufforderung zur Änderung Beliebiger Serverstatus. Der Client kann eine GET oder HEAD Anfrage 10 Mal stellen und es ist dasselbe wie einmal oder macht es überhaupt nicht.

Idempotenz: Eine idempotente Operation in einer, die den gleichen Effekt hat, unabhängig davon, ob Sie sie einmal oder mehrmals anwenden (in der Mathematik ist das Multiplizieren mit Null idempotent). Wenn Sie eine Ressource einmal DELETE löschen, hat dies den gleichen Effekt (die Ressource ist bereits GONE).

POST ist weder sicher noch idempotent. Wenn Sie zwei identische POST Anforderungen an eine 'Factory'-Ressource senden, werden wahrscheinlich zwei untergeordnete Ressourcen mit denselben Informationen erstellt. Bei Überladung (Methode in URI oder Entity-Body) POST sind alle Wetten deaktiviert.

Diese beiden Eigenschaften waren wichtig für den Erfolg des HTTP-Protokolls (über unzuverlässige Netzwerke!): Wie oft haben Sie die Seite aktualisiert (GET), ohne zu warten, bis sie vollständig geladen ist?

Das Erstellen einer Aktion und das Platzieren in der URL unterbricht eindeutig den Vertrag der HTTP-Methoden. Noch einmal, die Technologie ermöglicht es Ihnen, Sie können es tun, aber das ist kein REST-konformes Design.

265
acdcjunior

I früher beantwortet , aber diese Antwort widerspricht meiner alten Antwort und folgt einer ganz anderen Strategie, um zu einer Lösung zu kommen. Es zeigt Wie die HTTP-Anforderung aus den Konzepten erstellt wird, die REST und HTTP definieren. Sie verwendet auch PATCH anstelle von POST oder PUT.

Es geht durch die REST Einschränkungen, dann die Komponenten von HTTP, dann eine mögliche Lösung.

REST

REST ist eine Reihe von Einschränkungen, die auf ein verteiltes Hypermediensystem angewendet werden sollen, um es skalierbar zu machen. Auch wenn dies im Kontext der Remotesteuerung einer Aktion sinnvoll ist, müssen Sie sich die Remotesteuerung einer Aktion als Teil eines verteilten Hypermediensystems vorstellen - als Teil eines Systems zum Erkennen, Anzeigen und Ändern von miteinander verbundenen Informationen. Wenn das mehr Mühe ist als es wert ist, dann ist es wahrscheinlich nicht gut zu versuchen, es RESTful zu machen. Wenn Sie nur eine Benutzeroberfläche vom Typ "Systemsteuerung" auf dem Client benötigen, die über Port 80 Aktionen auf dem Server auslösen kann, benötigen Sie wahrscheinlich eine einfache RPC-Schnittstelle wie JSON-RPC über HTTP-Anforderungen/Antworten oder ein WebSocket.

Aber REST ist eine faszinierende Denkweise und das Beispiel in der Frage ist einfach mit einer REST-Schnittstelle zu modellieren. Nehmen wir also die Herausforderung zum Spaß und zur Bildung an.

REST ist definiert durch vier Schnittstellenbeschränkungen:

identifizierung von Ressourcen; Manipulation von Ressourcen durch Repräsentationen; selbstbeschreibende Botschaften; und Hypermedien als Motor des Anwendungszustands.

Sie fragen, wie Sie eine Schnittstelle definieren können, die diese Einschränkungen erfüllt und über die ein Computer einem anderen Computer anweist, einen Hund zum Bellen zu bringen. Insbesondere soll Ihre Schnittstelle HTTP sein, und Sie möchten nicht die Funktionen verwerfen, die HTTP-REST-fähig machen, wenn es bestimmungsgemäß verwendet wird.

Beginnen wir mit der ersten Einschränkung: Ressourcenidentifikation .

Jede Information, die benannt werden kann, kann eine Ressource sein: ein Dokument oder ein Bild, ein zeitlicher Dienst (z. B. "das heutige Wetter in Los Angeles"), eine Sammlung anderer Ressourcen, ein nicht virtuelles Objekt (z. B. eine Person) usw. .

Ein Hund ist also eine Ressource. Es muss identifiziert werden.

Genauer gesagt, eine Ressource R ist eine zeitlich variierende Zugehörigkeitsfunktion MR(t), die für die Zeit t Ordnet eine Reihe von Entitäten oder Werten zu, die gleichwertig sind. Die Werte in der Menge können Ressourcendarstellungen und/oder Ressourcenkennungen sein.

Sie model ein Hund, indem Sie eine Reihe von Kennungen und Darstellungen nehmen und sagen, dass sie alle zu einem bestimmten Zeitpunkt miteinander verbunden sind. Verwenden wir zunächst den Bezeichner "Hund # 1". Das bringt uns zu den zweiten und dritten Einschränkungen: Ressourcendarstellung und Selbstbeschreibung .

REST-Komponenten führen Aktionen für eine Ressource aus, indem sie mithilfe einer Repräsentation den aktuellen oder beabsichtigten Status dieser Ressource erfassen und diese Repräsentation zwischen Komponenten übertragen. Eine Repräsentation ist eine Folge von Bytes plus Repräsentationsmetadaten zur Beschreibung dieser Bytes.

Das Folgende ist eine Folge von Bytes, die den beabsichtigten Zustand des Hundes erfassen, dh die Darstellung, die wir mit der Kennung "Hund Nr. 1" assoziieren möchten (beachten Sie, dass sie nur einen Teil des Zustands darstellt, da der Name des Hundes und die Gesundheit nicht berücksichtigt werden oder sogar nach Bellen):

Es hat alle 10 Minuten gebellt, seit dieser Zustandswechsel durchgeführt wurde, und wird auf unbestimmte Zeit fortgesetzt.

Es soll an Metadaten angehängt werden, die es beschreiben. Diese Metadaten könnten nützlich sein:

Es ist eine englische Aussage. Es beschreibt einen Teil des beabsichtigten Zustands. Wenn es mehrmals empfangen wird, lassen Sie nur das erste zu, um einen Effekt zu haben.

Schauen wir uns zum Schluss die vierte Einschränkung an: [~ # ~] hateoas [~ # ~] .

REST ... betrachtet eine Anwendung als eine zusammenhängende Struktur von Informationen und Steuerungsalternativen, über die ein Benutzer eine gewünschte Aufgabe ausführen kann. Ein Wort in einem Online-Wörterbuch nachzuschlagen ist beispielsweise eine Anwendung, ebenso wie durch ein virtuelles Museum zu touren oder eine Reihe von Notizen zu lesen, um für eine Prüfung zu lernen. ... Der nächste Steuerungszustand einer Anwendung liegt in der Darstellung der ersten angeforderten Ressource, so dass das Erhalten dieser ersten Darstellung eine Priorität ist. ... Die Modellanwendung ist daher eine Engine, die sich von einem Zustand zum nächsten bewegt, indem sie alternative Zustandsübergänge in der aktuellen Menge von Darstellungen untersucht und aus diesen auswählt.

In einer RESTful-Schnittstelle erhält der Client eine Ressourcendarstellung, um herauszufinden, wie er eine Darstellung empfangen oder senden soll. Die Anwendung muss irgendwo eine Darstellung enthalten, anhand derer der Kunde herausfinden kann, wie er alle Darstellungen empfangen oder senden kann, die er empfangen oder senden kann, auch wenn er einer Reihe von Darstellungen folgt, um zu diesen Informationen zu gelangen. Das scheint einfach zu sein:

Der Kunde fragt nach einer Darstellung einer Ressource, die als Homepage identifiziert wurde. Als Antwort erhält es eine Darstellung, die eine Kennung jedes Hundes enthält, den der Kunde möglicherweise wünscht. Der Kunde extrahiert eine Kennung daraus und fragt den Dienst, wie er mit dem identifizierten Hund interagieren kann, und der Dienst sagt, der Kunde kann eine englische Erklärung senden, die einen Teil des beabsichtigten Zustands des Hundes beschreibt. Dann sendet der Client eine solche Anweisung und erhält eine Erfolgsmeldung oder eine Fehlermeldung.

HTTP

HTTP implementiert REST Einschränkungen wie folgt:

Ressourcenidentifikation : URI

Ressourcendarstellung : Entity-Body

Selbstbeschreibung : Methode oder Statuscode, Header und möglicherweise Teile des Entity-Body (z. B. der URI eines XML-Schemas)

[~ # ~] Hass [~ # ~] : Hyperlinks

Sie haben sich für http://api.animals.com/v1/dogs/1 Als URI entschieden. Nehmen wir an, der Kunde hat dies von einer Seite auf der Website erhalten.

Verwenden wir diesen Entity-Body (der Wert von next ist ein Zeitstempel; ein Wert von 0 Bedeutet 'wenn diese Anfrage eingeht'):

{"barks": {"next": 0, "frequency": 10}}

Jetzt brauchen wir eine Methode. PATCH passt zu der Beschreibung "Teil des beabsichtigten Zustands", für die wir uns entschieden haben:

Die PATCH-Methode fordert an, dass eine Reihe von Änderungen, die in der Anforderungsentität beschrieben sind, auf die durch den Anforderungs-URI angegebene Ressource angewendet werden.

Und einige Überschriften:

So geben Sie die Sprache des Entitätskörpers an: Content-Type: application/json

Um sicherzustellen, dass es nur einmal vorkommt: If-Unmodified-Since: <date/time this was first sent>

Und wir haben eine Anfrage:

PATCH /v1/dogs/1/ HTTP/1.1
Host: api.animals.com
Content-Type: application/json
If-Unmodified-Since: <date/time this was first sent>
[other headers]

{"barks": {"next": 0, "frequency": 10}}

Bei Erfolg sollte der Client einen 204 Statuscode als Antwort erhalten, oder einen 205, Wenn sich die Darstellung von /v1/dogs/1/ Geändert hat, um der neuen Rechnung zu tragen bellender Zeitplan.

Bei einem Fehler sollte ein 403 Und eine hilfreiche Meldung angezeigt werden, warum.

Es ist nicht unbedingt erforderlich, REST für den Service, um den Rindenzeitplan in einer Darstellung als Antwort auf GET /v1/dogs/1/ Wiederzugeben, aber es wäre am sinnvollsten, wenn eine JSON-Darstellung Folgendes enthält:

"barks": {
    "previous": [x_1, x_2, ..., x_n],
    "next": x_n,
    "frequency": 10
}

Behandeln Sie den Cron-Job als Implementierungsdetail, das der Server vor der Schnittstelle verbirgt. Das ist das Schöne an einer generischen Benutzeroberfläche. Der Client muss nicht wissen, was der Server hinter den Kulissen tut. Alles, was es interessiert, ist, dass der Service die angeforderten Statusänderungen versteht und darauf reagiert.

6
Jordan

Die meisten Leute benutzen [~ # ~] Post [~ # ~] für diesen Zweck. Es ist für die Ausführung von "unsicheren oder nicht temporären Vorgängen geeignet, wenn keine andere HTTP-Methode geeignet erscheint".

APIs wie XMLRPC use [~ # ~] post [~ # ~] , um Aktionen auszulösen, die beliebigen Code ausführen können. Die "Aktion" ist in den POST Daten enthalten:

POST /RPC2 HTTP/1.0
User-Agent: Frontier/5.1.2 (WinNT)
Host: betty.userland.com
Content-Type: text/xml
Content-length: 181

<?xml version="1.0"?>
<methodCall>
   <methodName>examples.getStateName</methodName>
   <params>
      <param>
         <value><i4>41</i4></value>
         </param>
      </params>
   </methodCall>

Der RPC ist ein Beispiel, das zeigt, dass POST ist die konventionelle Wahl von HTTP-Verben für serverseitige Methoden. Hier ist Roy Fielding Gedanken zu POST - er ziemlich viel sagt, dass es RESTful ist, die angegebenen HTTP-Methoden zu verwenden.

Beachten Sie, dass RPC selbst nicht sehr restvoll ist, da es nicht ressourcenorientiert ist. Wenn Sie jedoch Staatenlosigkeit, Caching oder Layering benötigen, ist es nicht schwierig, die entsprechenden Transformationen vorzunehmen. Ein Beispiel finden Sie unter http://blog.perfectapi.com/2012/opinionated-rpc-apis-vs-restful-apis/ .

3

POST ist die HTTP-Methode für

Bereitstellung eines Datenblocks ... für einen Datenverarbeitungsprozess

Serverseitige Methoden zur Behandlung von nicht CRUD-gemappten Aktionen sind das, was Roy Fielding beabsichtigte mit REST, also sind Sie gut da, und deshalb wurde POST so gemacht, dass es nicht idempotent ist. POST verarbeitet die meisten Daten zum Verarbeiten der Informationen über serverseitige Methoden.

Das heißt, wenn Sie in Ihrem Hundebell-Szenario alle 10 Minuten eine serverseitige Barke ausführen möchten, aber aus irgendeinem Grund den Auslöser benötigen, der von einem Client stammt, würde PUT den Zweck besser erfüllen. wegen seiner Idempotenz. Nun, streng genommen besteht in diesem Szenario kein offensichtliches Risiko, dass mehrere POST) Anfragen Ihren Hund zum Miauen bringen, aber auf jeden Fall ist dies der Zweck der beiden ähnlichen Methoden. Meine Antwort auf eine ähnliche SO Frage kann für Sie nützlich sein.

2
Jacob Stevens

In früheren Versionen einiger Antworten wurde empfohlen, RPC zu verwenden. Sie müssen nicht auf RPC schauen, da es ist vollkommen möglich ist, das zu tun, was Sie wollen, während Sie die REST -Einschränkungen einhalten.

Fügen Sie zunächst keine Aktionsparameter in die URL ein. Die URL definiert was, auf das Sie die Aktion anwenden, und Abfrageparameter sind Teil der URL. Es sollte vollständig als Nomen betrachtet werden. http://api.animals.com/v1/dogs/1/?action=bark Ist eine andere Ressource - ein anderes Nomen - als http://api.animals.com/v1/dogs/1/ . [n.b. Asker hat den URI ?action=bark Aus der Frage entfernt.] Vergleichen Sie beispielsweise http://api.animals.com/v1/dogs/?id=1 Mit http://api.animals.com/v1/dogs/?id=2. Verschiedene Ressourcen, die sich nur durch die Abfragezeichenfolge unterscheiden. Die Aktion Ihrer Anforderung muss daher im Anforderungshauptteil definiert werden, es sei denn, sie entspricht direkt einem körperlos vorhandenen Methodentyp (TRACE, OPTIONS, HEAD, GET, DELETE usw.).

Entscheiden Sie als Nächstes, ob die Aktion " idempotent " ist, was bedeutet, dass sie ohne nachteilige Auswirkungen wiederholt werden kann (weitere Erläuterungen finden Sie im nächsten Absatz). Das Setzen eines Werts auf true kann beispielsweise wiederholt werden, wenn der Client nicht sicher ist, ob der gewünschte Effekt eingetreten ist. Sie senden die Anfrage erneut und der Wert bleibt wahr. Das Hinzufügen von 1 zu einer Zahl ist nicht idempotent. Wenn der Client den Befehl Add1 sendet, nicht sicher ist, ob er funktioniert hat, und ihn erneut sendet, hat der Server ein oder zwei Befehle hinzugefügt? Sobald Sie dies festgestellt haben, können Sie für Ihre Methode besser zwischen PUT und POST wählen.

Idempotent bedeutet, dass eine Anfrage wiederholt werden kann, ohne das Ergebnis zu ändern. Diese Effekte umfassen nicht die Protokollierung und andere solche Serveradministrationsaktivitäten. Wenn Sie Ihr erstes und zweites Beispiel verwenden, führt das Senden von zwei E-Mails an dieselbe Person zu einem anderen Status als das Senden einer E-Mail (der Empfänger hat zwei E-Mails im Posteingang, die er möglicherweise als Spam ansieht). Daher würde ich auf jeden Fall POST dafür. Wenn der barkCount in Beispiel 2 für einen Benutzer Ihrer API sichtbar sein soll oder sich auf etwas auswirkt, das für den Client sichtbar ist, ist die Anforderung auch nicht idempotent. Wenn es nur von Ihnen angezeigt werden soll, gilt es als Serverprotokollierung und sollte bei der Ermittlung der Idempotenz ignoriert werden.

Stellen Sie abschließend fest, ob die Aktion, die Sie ausführen möchten, voraussichtlich sofort erfolgreich sein wird oder nicht. BarkDog ist eine schnell erledigte Aktion. RunMarathon ist nicht. Wenn Ihre Aktion langsam ist, können Sie einen 202 Accepted Mit einer URL im Antworttext zurückgeben, damit ein Benutzer abfragen kann, ob die Aktion abgeschlossen ist. Alternativ können Sie die Benutzer POST zu einer Listen-URL wie /marathons-in-progress/ Führen. Wenn die Aktion abgeschlossen ist, leiten Sie sie von der laufenden ID-URL zur URL /marathons-complete/ Um.
In den speziellen Fällen 1 und 2 sollte der Server eine Warteschlange bereitstellen und der Client mehrere Adressen an die Warteschlange senden. Die Aktion wäre nicht SendEmails, sondern so etwas wie AddToDispatchQueue. Der Server kann dann die Warteschlange abfragen, um festzustellen, ob noch E-Mail-Adressen vorhanden sind, und gegebenenfalls E-Mails senden. Anschließend wird die Warteschlange aktualisiert, um anzuzeigen, dass die ausstehende Aktion jetzt ausgeführt wurde. Sie hätten einen anderen URI, der dem Client den aktuellen Status der Warteschlange anzeigt. Um ein doppeltes Senden von E-Mails zu vermeiden, könnte der Server auch ein Protokoll darüber führen, an wen er diese E-Mail gesendet hat, und jede Adresse mit dieser abgleichen, um sicherzustellen, dass niemals zwei an dieselbe Adresse gesendet werden, auch wenn Sie POST die gleiche Liste zweimal in die Warteschlange.

Wenn Sie einen URI für etwas auswählen, versuchen Sie, ihn als Ergebnis und nicht als Aktion zu betrachten. Zum Beispiel zeigt google.com/search?q=dogs Die Ergebnisse einer Suche nach dem Wort "dogs". Die Suche muss nicht durchgeführt werden.

Die Fälle 3 und 4 aus Ihrer Liste sind ebenfalls keine idempotenten Aktionen. Sie schlagen vor, dass sich die verschiedenen vorgeschlagenen Effekte auf das API-Design auswirken könnten. In allen vier Fällen würde ich dieselbe API verwenden, da alle vier den "Weltzustand" ändern.

1
Nicholas Shanks

Wenn wir annehmen, dass das Bellen eine innere/abhängige/untergeordnete Ressource ist, auf die der Verbraucher einwirken kann, können wir sagen:

POST http://api.animals.com/v1/dogs/1/bark

hund Nummer 1 bellt

GET http://api.animals.com/v1/dogs/1/bark

gibt den letzten Rinden-Zeitstempel zurück

DELETE http://api.animals.com/v1/dogs/1/bark

trifft nicht zu! also ignoriere es.

1
bolbol

Siehe meine neue Antwort - es widerspricht dieser und erklärt REST und HTTP mehr klar und genau.

Hier ist eine Empfehlung , die zufällig RESTful ist, aber sicherlich nicht die einzige Option ist. So fangen Sie an zu bellen, wenn der Dienst die Anfrage erhält:

POST /v1/dogs/1/bark-schedule HTTP/1.1
...
{"token": 12345, "next": 0, "frequency": 10}

token ist eine beliebige Zahl, die redundantes Bellen verhindert, unabhängig davon, wie oft diese Anforderung gesendet wird.

next gibt die Zeit der nächsten Rinde an; ein Wert von 0 bedeutet "ASAP".

Wann immer du GET /v1/dogs/1/bark-schedule, Sie sollten so etwas bekommen, wobei t die Zeit der letzten Rinde ist und u t + 10 Minuten:

{"last": t, "next": u}

Ich empfehle dringend, dass Sie dieselbe URL verwenden, um eine Barke anzufordern, die Sie verwenden, um den aktuellen Barkstatus des Hundes herauszufinden. Für REST ist dies nicht unbedingt erforderlich, betont jedoch das Ändern des Zeitplans.

Der entsprechende Statuscode lautet wahrscheinlich 205 . Ich stelle mir einen Client vor, der den aktuellen Zeitplan betrachtet, POSTs auf dieselbe URL verweist, um ihn zu ändern, und vom Service angewiesen wird, dem Zeitplan einen zweiten Blick zu geben, um zu beweisen, dass er geändert wurde.

Erläuterung

REST

Vergiss HTTP für einen Moment. Es ist wichtig zu verstehen, dass eine Ressource eine Funktion ist, die Zeit als Eingabe benötigt und eine Menge mit Bezeichnern und Darstellungen . Vereinfachen wir dies folgendermaßen: Eine Ressource ist eine Menge [~ # ~] r [~ # ~] von Bezeichnern und Darstellungen; [~ # ~] r [~ # ~] kann sich ändern - Mitglieder können hinzugefügt, entfernt oder geändert werden. (Obwohl es ein schlechtes, instabiles Design ist, Bezeichner zu entfernen oder zu ändern.) Wir sagen, ein Bezeichner, der ein Element von [~ # ~] r [~ # ~] ist, kennzeichnet [~ # ~] r [~ # ~] , und dass eine Darstellung, die ein Element von [~ # ~] r [~ # ~] steht für [~ # ~] r [~ # ~] .

Sagen wir [~ # ~] r [~ # ~] ist ein Hund. Sie identifizieren [~ # ~] r [~ # ~] als /v1/dogs/1. (Bedeutung /v1/dogs/1 ist ein Mitglied von [~ # ~] r [~ # ~] .) Dies ist nur eine von vielen Möglichkeiten, wie Sie [~ # ~] r [~ # ~] . Sie könnten auch [~ # ~] r [~ # ~] als /v1/dogs/1/x-rays und wie /v1/rufus.

Wie repräsentieren Sie [~ # ~] r [~ # ~] ? Vielleicht mit einem Foto. Vielleicht mit Röntgenstrahlen. Oder vielleicht mit Angabe von Datum und Uhrzeit, wann [~ # ~] r [~ # ~] zuletzt gebellt hat. Denken Sie jedoch daran, dass dies alles Darstellungen derselben Ressource sind. /v1/dogs/1/x-rays ist ein Bezeichner derselben Ressource, die durch eine Antwort auf die Frage "Wann hat [~ # ~] r [~ # ~] zuletzt gebellt?"

HTTP

Mehrere Darstellungen einer Ressource sind nicht sehr nützlich, wenn Sie sich nicht auf die gewünschte beziehen können. Das ist der Grund, warum HTTP nützlich ist: Sie können damit Bezeichner mit Repräsentationen verbinden . Dies ist eine Möglichkeit für den Service, eine URL zu erhalten und zu entscheiden, welche Darstellung dem Client bereitgestellt werden soll.

Zumindest ist es das, was GET tut. PUT ist im Grunde die Umkehrung von GET: Sie PUT eine Darstellung r an der URL, wenn Sie möchten future GET fordert diese URL auf, r mit einigen möglichen Übersetzungen wie JSON zu HTML zurückzugeben.

POST ist eine einfachere Methode zum Ändern einer Darstellung. Stellen Sie sich vor, es gäbe eine Anzeigelogik und eine Änderungslogik, die sich gegenüberstehen - beide entsprechen derselben URL. Eine POST Anfrage ist eine Anfrage an die Änderungslogik, um die Informationen zu verarbeiten und Repräsentationen (nicht nur die Repräsentation, die sich unter derselben URL befindet) zu ändern, wie es der Dienst für richtig hält. Beachten Sie den dritten Absatz after 9.6 PUT : Sie ersetzen die Sache an der URL nicht durch neuen Inhalt, Sie bitten die Sache an der URL, einige Informationen zu verarbeiten und intelligent in Form von informativen Darstellungen zu antworten.

In unserem Fall fragen wir die Änderungslogik bei /v1/dogs/1/bark-schedule (das ist das Gegenstück zur Anzeigelogik, die uns sagt, wann es zuletzt gebellt hat und wann es das nächste Mal bellt), um unsere Informationen zu verarbeiten und einige Darstellungen entsprechend zu ändern. In Reaktion auf zukünftige GETs teilt uns die Anzeigelogik, die derselben URL entspricht, mit, dass der Hund jetzt bellt, wie wir es wünschen.

Stellen Sie sich den Cron-Job als Implementierungsdetail vor. HTTP befasst sich mit dem Anzeigen und Ändern von Darstellungen. Von nun an teilt der Service dem Kunden mit, wann der Hund das letzte Mal gebellt hat und wann er das nächste Mal bellt. Aus Sicht des Dienstes ist das ehrlich, denn diese Zeiten stimmen mit früheren und geplanten Cron-Jobs überein.

0
Jordan