Denken Sie daran, ich habe ein rudimentäres Verständnis von REST. Angenommen, ich habe diese URL:
http://api.animals.com/v1/dogs/1/
Und jetzt möchte ich den Server dazu bringen, dass der Hund bellt. Nur der Server weiß, wie das geht. Nehmen wir an, ich möchte, dass es auf einem CRON-Job ausgeführt wird, bei dem der Hund für den Rest der Ewigkeit alle 10 Minuten bellt. Wie sieht dieser Anruf aus? Ich möchte das irgendwie tun:
URL-Anfrage:
ACTION http://api.animals.com/v1/dogs/1/
Im Anfragetext:
{"action":"bark"}
Bevor Sie sauer auf mich werden, weil ich meine eigene HTTP-Methode erfunden habe, helfen Sie mir und geben Sie mir eine bessere Vorstellung davon, wie ich eine serverseitige Methode auf RESTful-Weise aufrufen soll. :) :)
BEARBEITEN ZUR ERKLÄRUNG
Noch etwas Klarheit darüber, was die "Rinden" -Methode bewirkt. Hier sind einige Optionen, die zu unterschiedlich strukturierten API-Aufrufen führen können:
- bark sendet einfach eine E-Mail an dog.email und zeichnet nichts auf.
- bark sendet eine E-Mail an dog.email und erhöht dog.barkCount um 1.
- bark erstellt mit bark.timestamp einen neuen "bark" -Datensatz, wenn die Rinde aufgetreten ist. Außerdem wird dog.barkCount um 1 erhöht.
- 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 mitgeteilt wird, dass der neue Hundecode in Produktion ist.
quelle
PATCH
kann angemessen sein. Ich erkläre gegen Ende meiner Antwort , warum .Antworten:
Warum ein RESTful Design anstreben?
Die RESTful-Prinzipien bringen die Funktionen, die es Websites erleichtern (für einen zufälligen menschlichen Benutzer, sie zu "surfen"), in das API-Design der Webdienste ein , sodass sie für einen Programmierer einfach zu verwenden sind. REST ist nicht gut, weil es REST ist, es ist gut, weil es gut ist. Und es ist vor allem deshalb gut, weil es einfach ist .
Die Einfachheit von einfachem HTTP (ohne SOAP-Umschläge und überlastete Single-URI-
POST
Dienste), was manche als "Mangel an Funktionen" bezeichnen , ist tatsächlich seine größte Stärke . HTTP fordert Sie von Anfang an auf Adressierbarkeit und Statuslosigkeit : Die beiden grundlegenden Entwurfsentscheidungen, mit denen HTTP auf die heutigen Mega-Sites (und Mega-Services) skalierbar bleibt.Aber REST ist nicht das Beste: Manchmal kann ein RPC-Stil ("Remote Procedure Call" - wie SOAP) angebracht sein , und manchmal haben andere Anforderungen Vorrang vor den Vorzügen des Web. Das ist in Ordnung. Was wir nicht wirklich mögen, ist unnötige Komplexität . Zu oft bringt ein Programmierer oder ein Unternehmen RPC-ähnliche Dienste für einen Job ein, den einfaches altes HTTP problemlos verarbeiten kann. Der Effekt ist, dass HTTP für eine enorme XML-Nutzlast auf ein Transportprotokoll reduziert wird, das erklärt, was "wirklich" vor sich geht (nicht der URI oder die HTTP-Methode geben einen Hinweis darauf). Der resultierende Service ist viel zu komplex, kann nicht debuggt werden und funktioniert nur, wenn Ihre Clients genau das Setup haben, das der Entwickler beabsichtigt hat.
Ebenso wie ein Java / C # -Code nicht objektorientiert sein kann, macht die Verwendung von HTTP ein Design nicht RESTful. Man kann in der Eile gefangen sein, über ihre Dienste in Bezug auf Aktionen und entfernte Methoden nachzudenken , die aufgerufen werden sollten. Kein Wunder, dass dies meistens in einem RPC-Dienst (oder einem REST-RPC-Hybrid) endet. Der erste Schritt ist, anders zu denken. Ein RESTful-Design kann auf viele Arten erreicht werden. Eine Möglichkeit besteht darin , Ihre Anwendung in Bezug auf Ressourcen und nicht in Bezug auf Aktionen zu betrachten:
Ich werde unten Beispiele anführen. (Ein weiterer wichtiger Aspekt von REST ist die Verwendung von HATEOAS - ich putze es hier nicht, aber ich spreche in einem anderen Beitrag schnell darüber .)
Probleme des ersten Entwurfs
Werfen wir einen Blick auf das vorgeschlagene Design:
Zunächst sollten wir nicht in Betracht ziehen, ein neues HTTP-Verb (
ACTION
) zu erstellen . Im Allgemeinen ist dies aus mehreren Gründen unerwünscht :ACTION
Verb existiert?Lassen Sie uns nun die Verwendung in Betracht ziehen
POST
(ich werde unten diskutieren, warum, nehmen Sie jetzt einfach mein Wort dafür):Dies könnte in Ordnung sein ... aber nur wenn :
{"action":"bark"}
war ein Dokument; und/v1/dogs/1/
war ein "Dokumentenprozessor" (werkseitig) URI. Ein "Dokumentprozessor" ist eine URI, auf die Sie nur "werfen" und "vergessen" würden. Der Prozessor leitet Sie möglicherweise nach dem "Werfen" zu einer neu erstellten Ressource weiter. Beispiel: Der URI zum Posten von Nachrichten bei einem Nachrichtenbrokerdienst, der Sie nach dem Posten zu einem URI umleitet, der den Status der Nachrichtenverarbeitung anzeigt.Ich weiß nicht viel über Ihr System, aber ich würde bereits wetten, dass beide nicht wahr sind:
{"action":"bark"}
ist kein Dokument , sondern die Methode, mit der Sie versuchen, sich in den Dienst einzuschleichen. und/v1/dogs/1/
URI stellt eine "Hund" -Ressource dar (wahrscheinlich der Hund mitid==1
) und kein Dokumentenprozessor.Alles was wir jetzt wissen ist, dass das obige Design nicht so RESTful ist, aber was ist das genau? Was ist daran so schlimm? Grundsätzlich ist es schlecht, weil das eine komplexe URI mit komplexen Bedeutungen ist. Daraus kann man nichts schließen. Wie würde ein Programmierer wissen, dass ein Hund eine
bark
Aktion hat, die heimlich mit einem infundiertPOST
werden kann?Entwerfen der API-Aufrufe Ihrer Frage
Kommen wir also zur Sache und versuchen, diese Rinden RESTful zu gestalten, indem wir in Ressourcen denken . Gestatten Sie mir, das Buch Restful Web Services zu zitieren :
Nach der obigen Beschreibung können wir sehen, dass
bark
dies als Unterressource von adog
modelliert werden kann (da abark
in einem Hund enthalten ist, dh eine Rinde von einem Hund "gebellt" wird ).Aus dieser Überlegung haben wir bereits:
POST
/barks
Subressource of Dog :/v1/dogs/1/barks
, repräsentiert einebark
"Fabrik". Diese URI ist für jeden Hund eindeutig (da sie unter ist/v1/dogs/{id}
).Jetzt hat jeder Fall Ihrer Liste ein bestimmtes Verhalten.
1. bark sendet einfach eine E-Mail an
dog.email
und zeichnet nichts auf.Ist das Bellen (Senden einer E-Mail) eine synchrone oder asynchrone Aufgabe? Zweitens erfordert die
bark
Anfrage ein Dokument (möglicherweise die E-Mail) oder ist es leer?1.1 Rinde sendet eine E-Mail an
dog.email
und zeichnet nichts auf (als synchrone Aufgabe)Dieser Fall ist einfach. Ein Anruf bei der
barks
Werksressource führt sofort zu einer Rinde (einer gesendeten E-Mail) und die Antwort (ob OK oder nicht) wird sofort gegeben:Da es nichts aufzeichnet (ändert),
200 OK
ist 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
bark
Aufgabe zu verfolgen . Diebark
Aufgabe sollte dann eine Ressource mit einer eigenen URI sein:Auf diese Weise ist jeder
bark
nachvollziehbar. Der Client kann dann eineGET
an diebark
URI senden, um den aktuellen Status zu ermitteln. Vielleicht sogar ein verwendenDELETE
, um es abzubrechen.2. bark sendet eine E-Mail an
dog.email
und erhöht sich danndog.barkCount
um 1Dies kann schwieriger sein, wenn Sie dem Client mitteilen möchten, dass die
dog
Ressource geändert wird:In diesem Fall soll der
location
Header den Client wissen lassen, dass er einen Blick darauf werfen solldog
. Aus dem HTTP-RFC über303
:Wenn die Aufgabe asynchron ist,
bark
wird genau wie in der1.2
Situation eine Unterressource benötigt,303
die nach AbschlussGET .../barks/Y
der Aufgabe zurückgegeben werden sollte.3. Rinde erstellt einen neuen "
bark
" Datensatz mitbark.timestamp
Aufzeichnung, wenn die Rinde aufgetreten ist. Es wird auchdog.barkCount
um 1 erhöht .Hier
bark
wird das aufgrund der Anforderung erstellt, sodass der Status201 Created
angewendet wird.Wenn die Erstellung asynchron ist,
202 Accepted
ist stattdessen a erforderlich ( wie im HTTP-RFC angegeben ).Der gespeicherte Zeitstempel ist Teil der
bark
Ressource und kann mit einem darauf abgerufen werdenGET
. Der aktualisierte Hund kann auch darin "dokumentiert" werdenGET dogs/X/barks/Y
.4. bark führt einen Systembefehl aus, um die neueste Version des Hundecodes von Github abzurufen. Anschließend wird eine Textnachricht gesendet, in
dog.owner
der ihnen mitgeteilt wird, dass der neue Hundecode in Produktion ist.Der Wortlaut dieses Artikels ist kompliziert, aber es ist so ziemlich eine einfache asynchrone Aufgabe:
Der Client würde dann
GET
s ausgeben/v1/dogs/1/barks/a65h44
, um den aktuellen Status zu erfahren (wenn der Code abgerufen wurde, wurde die E-Mail an den Eigentümer gesendet und so weiter). Wann immer sich der Hund ändert,303
ist a anwendbar.Einpacken
Zitat von Roy Fielding :
In den obigen Beispielen
POST
ist einheitlich ausgelegt. Es wird den Hund "bark
" machen. Das ist weder sicher (was bedeutet, dass Rinde Auswirkungen auf die Ressourcen hat) noch idempotent (jede Anfrage ergibt eine neuebark
), was gut zumPOST
Verb passt .Ein Programmierer würde wissen: a,
POST
um a zubarks
ergebenbark
. Die Antwortstatuscodes (bei Bedarf auch mit Entity-Body und Headern) erläutern, was sich geändert hat und wie der Client vorgehen kann und sollte.Hinweis: Die wichtigsten verwendeten Quellen waren: " Restful Web Services " -Buch, HTTP RFC und Roy Fieldings Blog .
Bearbeiten:
Die Frage und damit die Antwort haben sich seit ihrer Erstellung erheblich geändert. Die ursprüngliche Frage zum Design einer URI lautet wie folgt:
Nachfolgend finden Sie eine Erklärung, warum dies keine gute Wahl ist:
Wie Clients dem Server mitteilen, was mit den Daten zu tun ist, sind die Methodeninformationen .
Welcher Teil der Daten [der Client möchte, dass der Server] bearbeitet wird, sind die Scoping-Informationen .
Nehmen Sie als Beispiel den URI von Google
http://www.google.com/search?q=DOG
. Dort sind die MethodeninformationenGET
und die Scoping-Informationen/search?q=DOG
.Um es kurz zu machen:
Und die Faustregel:
Sie können die "Rinde" "Aktion" in die URL (oder in den Entity-Body) einfügen und verwenden
POST
. Kein Problem, es funktioniert und ist vielleicht der einfachste Weg, dies zu tun, aber dies ist nicht RESTful .Um Ihren Service wirklich RESTful zu halten, müssen Sie möglicherweise einen Schritt zurücktreten und überlegen, was Sie hier wirklich tun möchten (welche Auswirkungen dies auf die Ressourcen haben wird).
Ich kann nicht über Ihre spezifischen Geschäftsanforderungen sprechen, aber ich möchte Ihnen ein Beispiel geben: Betrachten Sie einen RESTful-Bestellservice, bei dem Bestellungen wie bei URIs erfolgen
example.com/order/123
.Angenommen, wir möchten eine Bestellung stornieren. Wie machen wir das? Man könnte versucht sein zu glauben, dass dies eine "Stornierung", "Aktion" ist, und sie als zu gestalten
POST example.com/order/123?do=cancel
.Das ist nicht RESTful, wie wir oben gesprochen haben. Stattdessen könnten wir
PUT
eine neue Darstellung vonorder
mit einemcanceled
Element senden, das antrue
folgende Adresse gesendet wird :Und das ist es. Wenn die Bestellung nicht storniert werden kann, kann ein bestimmter Statuscode zurückgegeben werden. (Der Einfachheit halber kann auch ein Subressourcen-Design wie
POST /order/123/canceled
beim Entity-Bodytrue
verfügbar sein.)In Ihrem speziellen Szenario können Sie etwas Ähnliches ausprobieren. Auf diese Weise könnte ein
GET
at , während beispielsweise ein Hund bellt,/v1/dogs/1/
diese Informationen enthalten (z<barking>true</barking>
. B. ) . Oder ... wenn das zu kompliziert ist, lockern Sie Ihre RESTful-Anforderung und bleiben Sie dabeiPOST
.Aktualisieren:
Ich möchte die Antwort nicht zu groß machen, aber es dauert eine Weile, bis ich den Dreh raus habe, einen Algorithmus (eine Aktion ) als eine Reihe von Ressourcen verfügbar zu machen. Anstatt in Aktionen zu denken ( "Suche nach Orten auf der Karte" ), muss man in den Ergebnissen dieser Aktion denken ( "die Liste der Orte auf der Karte, die einem Suchkriterium entsprechen" ).
Möglicherweise kehren Sie zu diesem Schritt zurück, wenn Sie feststellen, dass Ihr Design nicht zur einheitlichen HTTP-Oberfläche passt.
Query - Variablen sind Scoping Informationen , aber nicht bezeichnen neue Ressourcen (
/post?lang=en
ist eindeutig die gleiche Ressource wie/post?lang=jp
, nur eine andere Darstellung). Sie werden vielmehr verwendet, um den Client-Status (z. B.?page=10
damit der Status nicht auf dem Server gespeichert wird; dies?lang=en
ist auch hier ein Beispiel) oder Eingabeparameter für algorithmische Ressourcen (/search?q=dogs
,/dogs?code=1
) zu übermitteln . Wieder keine unterschiedlichen Ressourcen.Eigenschaften von HTTP-Verben (Methoden):
Ein weiterer klarer Punkt, der
?action=something
in der URI angezeigt wird, ist nicht RESTful. Dies sind die Eigenschaften von HTTP-Verben:GET
undHEAD
sind sicher (und idempotent);PUT
undDELETE
sind nur idempotent;POST
ist weder.Sicherheit : Eine
GET
oderHEAD
Anforderung ist eine Anforderung zum Lesen einiger Daten, keine Anforderung zum Ändern eines Serverstatus. Der Client kann 10 Mal eineGET
oder eineHEAD
Anfrage stellen, und es ist dasselbe wie einmal oder gar 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
DELETE
eine Ressource einmal haben, hat das erneute Löschen den gleichen Effekt (die Ressource istGONE
bereits vorhanden).POST
ist weder sicher noch idempotent. Wenn Sie zwei identischePOST
Anforderungen an eine 'Factory'-Ressource stellen, werden wahrscheinlich zwei untergeordnete Ressourcen dieselbe Information enthalten. Bei Überlastung (Methode in URI oder Entity-Body)POST
sind alle Wetten deaktiviert.Beide Eigenschaften waren wichtig für den Erfolg des HTTP-Protokolls (über unzuverlässige Netzwerke!): Wie oft haben Sie
GET
die Seite aktualisiert ( ), ohne zu warten, bis sie vollständig geladen ist?Das Erstellen und Einfügen einer Aktion in die URL bricht eindeutig den Vertrag der HTTP-Methoden. Die Technologie ermöglicht es Ihnen erneut, dies zu tun, aber das ist kein RESTful-Design.
quelle
POST
wurde entwickelt, um "einen Datenblock ... für einen Datenverarbeitungsprozess bereitzustellen" . Es scheint, dass viele Leute Ressourcen von Aktionen unterscheiden, aber tatsächlich sind Aktionen nur eine Art von Ressource.POST
„einen Block von Daten ... zu einer Datenverarbeitungsprozess bereitstellt“, aber der Unterschied ist wirklich , dass ein Block von Daten , nicht ein Block von Daten und die Prozedur (Aktion, Verfahren, Befehl) zu sein dann ausgeführt. Das istPOST
Überladung, undPOST
Überladung ist RPC-artiges Design, nicht RESTful.Ich habe früher geantwortet , aber diese Antwort widerspricht meiner alten Antwort und verfolgt eine ganz andere Strategie, um zu einer Lösung zu kommen. Es zeigt, wie die HTTP-Anforderung aus den Konzepten erstellt wird, die REST und HTTP definieren. Es wird auch
PATCH
anstelle vonPOST
oder verwendetPUT
.Es geht durch die REST-Einschränkungen, dann die Komponenten von HTTP, dann eine mögliche Lösung.
SICH AUSRUHEN
REST ist eine Reihe von Einschränkungen, die auf ein verteiltes Hypermedia-System angewendet werden sollen, um es skalierbar zu machen. Selbst um dies im Zusammenhang mit der Fernsteuerung einer Aktion zu verstehen, müssen Sie sich vorstellen, eine Aktion als Teil eines verteilten Hypermedia-Systems fernzusteuern - als Teil eines Systems zum Erkennen, Anzeigen und Ändern miteinander verbundener Informationen. Wenn das mehr Ärger ist als es wert ist, dann ist es wahrscheinlich nicht gut zu versuchen, es RESTful zu machen. Wenn Sie nur eine GUI vom Typ "Control Panel" auf dem Client möchten, die über Port 80 Aktionen auf dem Server auslösen kann, möchten Sie wahrscheinlich eine einfache RPC-Schnittstelle wie JSON-RPC über HTTP-Anforderungen / -Antworten oder einen WebSocket.
Aber REST ist eine faszinierende Denkweise und das Beispiel in der Frage ist einfach mit einer RESTful-Oberfläche zu modellieren. Nehmen wir also die Herausforderung zum Spaß und zur Bildung an.
REST wird durch vier Schnittstellenbeschränkungen definiert :
Sie fragen, wie Sie eine Schnittstelle definieren können, die diese Einschränkungen erfüllt und über die ein Computer einen anderen Computer anweist, einen Hund bellen zu lassen. Insbesondere möchten Sie, dass Ihre Schnittstelle HTTP ist, und Sie möchten nicht die Funktionen verwerfen, die HTTP bei bestimmungsgemäßer Verwendung RESTful machen.
Beginnen wir mit der ersten Einschränkung: Ressourcenidentifikation .
Ein Hund ist also eine Ressource. Es muss identifiziert werden.
Sie modellieren einen 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 die Kennung "Hund Nr. 1". Das bringt uns zu der zweiten und dritten Einschränkung: Ressourcendarstellung und Selbstbeschreibung .
Es folgt eine Folge von Bytes, die den beabsichtigten Zustand des Hundes erfassen, dh die Darstellung, die wir mit der Kennung "Hund Nr. 1" verknüpfen möchten (beachten Sie, dass sie nur einen Teil des Zustands darstellt, da der Name und die Gesundheit des Hundes nicht berücksichtigt werden oder sogar vergangene Rinden):
Es soll an Metadaten angehängt werden, die es beschreiben. Diese Metadaten könnten nützlich sein:
Schauen wir uns zum Schluss die vierte Einschränkung an: HATEOAS .
In einer RESTful-Schnittstelle erhält der Client eine Ressourcendarstellung, um herauszufinden, wie er eine Darstellung empfangen oder senden soll. Irgendwo in der Anwendung muss eine Darstellung vorhanden sein, von der der Client herausfinden kann, wie er alle Darstellungen empfangen oder senden kann, die er empfangen oder senden kann, selbst wenn er einer Kette von Darstellungen folgt, um zu diesen Informationen zu gelangen. Das scheint einfach zu sein:
Der Client fordert eine Darstellung einer als Homepage identifizierten Ressource an. Als Antwort erhält es eine Darstellung, die eine Kennung jedes Hundes enthält, den der Kunde möglicherweise möchte. Der Kunde extrahiert eine Kennung daraus und fragt den Dienst, wie er mit dem identifizierten Hund interagieren kann. Der Dienst sagt, dass der Kunde eine englische Erklärung senden kann, die einen Teil des beabsichtigten Zustands des Hundes beschreibt. Dann sendet der Client eine solche Anweisung und empfängt eine Erfolgsmeldung oder eine Fehlermeldung.
HTTP
HTTP implementiert REST-Einschränkungen wie folgt:
Ressourcenidentifikation : URI
Ressourcendarstellung : Entity-Body
Selbstbeschreibung : Methoden- oder Statuscode, Header und möglicherweise Teile des Entitätskörpers (z. B. der URI eines XML-Schemas)
HATEOAS : Hyperlinks
Sie haben sich
http://api.animals.com/v1/dogs/1
als URI entschieden. Nehmen wir an, der Client hat dies von einer Seite auf der Site erhalten.Verwenden wir diesen Entitätskörper (der Wert von
next
ist ein Zeitstempel; ein Wert von0
bedeutet "wenn diese Anforderung empfangen wird"):Jetzt brauchen wir eine Methode. PATCH passt zu der Beschreibung "Teil des beabsichtigten Zustands", für die wir uns entschieden haben:
Und einige Überschriften:
So geben Sie die Sprache des Entitätskörpers an:
Content-Type: application/json
Um sicherzustellen, dass es nur einmal passiert:
If-Unmodified-Since: <date/time this was first sent>
Und wir haben eine Anfrage:
Bei Erfolg sollte der Kunde als
204
Antwort einen Statuscode erhalten oder eine,205
wenn sich die Darstellung von/v1/dogs/1/
geändert hat, um den neuen Bellenplan widerzuspiegeln.Bei einem Fehler sollte eine
403
und eine hilfreiche Meldung angezeigt werden, warum.Für REST ist es nicht unbedingt erforderlich, dass der Service den Rindenplan in einer Darstellung als Antwort auf widerspiegelt
GET /v1/dogs/1/
, aber es wäre am sinnvollsten, wenn eine JSON-Darstellung Folgendes enthalten würde: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 Dienst angeforderte Statusänderungen versteht und darauf reagiert.
quelle
Die meisten Leute benutzen POST für diesen Zweck. Es ist geeignet, um "unsichere oder nicht epidempotente Operationen durchzuführen, wenn keine andere HTTP-Methode angemessen erscheint".
APIs wie XMLRPC verwenden POST , um Aktionen auszulösen, mit denen beliebiger Code ausgeführt werden kann. Die "Aktion" ist in den POST-Daten enthalten:
Das RPC-Beispiel soll zeigen, dass POST die herkömmliche Wahl von HTTP-Verben für serverseitige Methoden ist. Hier sind Roy Fieldings Gedanken zu POST - er sagt ziemlich genau, dass es RESTful ist, die angegebenen HTTP-Methoden zu verwenden.
Beachten Sie, dass RPC selbst nicht sehr REST-fähig ist, da es nicht ressourcenorientiert ist. Wenn Sie jedoch Staatenlosigkeit, Caching oder Layering benötigen, ist es nicht schwer, die entsprechenden Transformationen vorzunehmen. Ein Beispiel finden Sie unter http://blog.perfectapi.com/2012/opinionated-rpc-apis-vs-restful-apis/ .
quelle
POST api.animals.com/v1/dogs1?action=bark
/RPC2
nichts zur Identifizierung einer Ressource beiträgt - sie identifiziert eine Servertechnologie. Stattdessen wirdmethodName
versucht, die 'Ressource' zu 'identifizieren' - aber selbst dann profitiert es nicht von der Unterscheidung zwischen Substantiv und Verb. Das einzige, was hier wie ein Verb aussieht, istmethodCall
. Dies ist wie "Statusnamen abrufen" anstelle von "Statusnamen abrufen" - letzteres ist viel sinnvoller.POST
ist die HTTP-Methode fürServerseitige Methoden, die nicht CRUD-zugeordnete Aktionen verarbeiten, sind das, was Roy Fielding mit REST beabsichtigt hat. Sie sind also gut darin, und deshalb
POST
wurde es so gemacht, dass es nicht idempotent ist.POST
behandelt die meisten Daten an serverseitige Methoden, um die Informationen zu verarbeiten.Das heißt, wenn Sie in Ihrem Szenario mit Hundebellen alle 10 Minuten ein serverseitiges Bellen durchführen möchten, aber aus irgendeinem Grund der Auslöser von einem Client stammen muss,
PUT
würde dies aufgrund seiner Idempotenz den Zweck besser erfüllen. Genau genommen besteht in diesem Szenario kein offensichtliches Risiko, dass mehrere POST-Anfragen dazu führen, dass Ihr Hund stattdessen miaut, aber das ist trotzdem der Zweck der beiden ähnlichen Methoden. Meine Antwort auf eine ähnliche SO-Frage kann für Sie nützlich sein.quelle
PUT
URL bezieht sich auf das, was sollte werden ersetzt durch die Client-Inhalt und diePOST
URL verweist auf das, was sollte verarbeiten des Kunden Inhalt aber es will.Wenn wir annehmen, dass Barking eine innere / abhängige / untergeordnete Ressource ist, auf die der Verbraucher reagieren kann, können wir sagen:
Hund Nummer 1 bellt
Gibt den letzten Rindenzeitstempel zurück
trifft nicht zu! Also ignoriere es.
quelle
/v1/dogs/1/bark
eine Ressource an sich betrachten undPOST
beschreiben, wie sich der interne Status dieser Ressource ändern sollte. Ich finde es sinnvoller, nur/v1/dogs/1/
als Ressource zu betrachten und im Entitätskörper anzugeben, dass es bellen sollte.Frühere Überarbeitungen einiger Antworten deuteten darauf hin, dass Sie RPC verwenden. Sie müssen nicht auf RPC schauen müssen , wie es ist durchaus möglich , zu tun , was Sie wollen , während Einschränkungen für den Rest haften.
Fügen Sie zunächst keine Aktionsparameter in die URL ein. Die URL definiert, auf was Sie die Aktion anwenden, und Abfrageparameter sind Teil der URL. Es sollte ganz als Substantiv betrachtet werden.
http://api.animals.com/v1/dogs/1/?action=bark
ist eine andere Ressource - ein anderes Substantiv - zuhttp://api.animals.com/v1/dogs/1/
. [nb Asker hat den?action=bark
URI aus der Frage entfernt.] Vergleichen Sie beispielsweisehttp://api.animals.com/v1/dogs/?id=1
mithttp://api.animals.com/v1/dogs/?id=2
. Unterschiedliche Ressourcen, die nur durch die Abfragezeichenfolge unterschieden werden. Daher muss die Aktion Ihrer Anfrage im Anfragetext definiert werden, es sei denn, sie entspricht direkt einem körperlosen 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 dann einen oder zwei hinzugefügt? Sobald Sie dies festgestellt haben, können Sie besser zwischen
PUT
undPOST
für Ihre Methode wählen .Idempotent bedeutet, dass eine Anfrage wiederholt werden kann, ohne das Ergebnis zu ändern. Diese Effekte umfassen nicht die Protokollierung und andere derartige 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 in seinem Posteingang, die er möglicherweise als Spam betrachtet). Daher würde ich POST definitiv dafür verwenden . Wenn der barkCount in Beispiel 2 von einem Benutzer Ihrer API gesehen werden soll oder sich auf etwas auswirkt, das für den Client sichtbar ist, würde dies auch die Anforderung nicht idempotent machen. Wenn es nur von Ihnen angezeigt werden soll, zählt 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, sofort erfolgreich sein kann oder nicht. BarkDog ist eine schnell abschließende Aktion. RunMarathon ist nicht. Wenn Ihre Aktion langsam ist, sollten Sie eine
202 Accepted
mit einer URL im Antworttext zurückgeben, die ein Benutzer abfragen kann, um festzustellen, ob die Aktion abgeschlossen ist. Alternativ können Benutzer eine POST an eine Listen-URL wie senden/marathons-in-progress/
und diese dann nach Abschluss der Aktion von der URL der laufenden ID zur/marathons-complete/
URL umleiten .Für die speziellen Fälle Nr. 1 und Nr. 2 würde der Server eine Warteschlange hosten und der Client Stapel von Adressen an diese senden. Die Aktion wäre nicht SendEmails, sondern so etwas wie AddToDispatchQueue. Der Server kann dann die Warteschlange abfragen, um festzustellen, ob E-Mail-Adressen warten, und E-Mails senden, wenn er welche findet. 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, kann der Server auch ein Protokoll darüber führen, an wen er diese E-Mail gesendet hat, und jede Adresse mit dieser vergleichen, um sicherzustellen, dass niemals zwei an dieselbe Adresse gesendet werden, selbst wenn Sie dieselbe Liste zweimal an POST senden die Warteschlange.
Versuchen Sie bei der Auswahl eines URI für irgendetwas, ihn als Ergebnis und nicht als Aktion zu betrachten. Zum Beispiel
google.com/search?q=dogs
zeigt die Ergebnisse einer Suche nach dem Wort "Hunde". Die Suche muss nicht unbedingt durchgeführt werden.Die Fälle Nr. 3 und Nr. 4 aus Ihrer Liste sind ebenfalls keine idempotenten Aktionen. Sie schlagen vor, dass die verschiedenen vorgeschlagenen Effekte das API-Design beeinflussen könnten. In allen vier Fällen würde ich dieselbe API verwenden, da alle vier den "Weltzustand" ändern.
quelle
Siehe meine neue Antwort - sie widerspricht dieser und erklärt REST und HTTP klarer und genauer.
Hier ist eine Empfehlung , die zwar RESTful ist, aber sicherlich nicht die einzige Option ist. So beginnen Sie zu bellen, wenn der Dienst die Anfrage erhält:
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 von0
bedeutet "ASAP".Jedes Mal , wenn Sie
GET /v1/dogs/1/bark-schedule
, sollten Sie etwas wie diese erhalten, wo t ist die Zeit der letzten Rinde und u ist t + 10 Minuten:{"last": t, "next": u}
Ich empfehle dringend, dass Sie dieselbe URL verwenden, um eine Rinde anzufordern, mit der Sie sich über den aktuellen Bellenzustand des Hundes informieren. Für REST ist dies nicht unbedingt erforderlich, es wird jedoch die Änderung des Zeitplans betont.
Der entsprechende Statuscode ist wahrscheinlich 205 . Ich stelle mir einen Client vor, der sich den aktuellen Zeitplan ansieht,
POST
dieselbe URL verwendet, um ihn zu ändern, und vom Dienst angewiesen wird, dem Zeitplan einen zweiten Blick zu geben, um zu beweisen, dass er geändert wurde.Erläuterung
SICH AUSRUHEN
Vergessen Sie für einen Moment HTTP. Es ist wichtig zu verstehen, dass eine Ressource eine Funktion ist, die Zeit als Eingabe benötigt und einen Satz zurückgibt, der Bezeichner und Darstellungen enthält . 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 schlecht ist , instabil Design zu entfernen oder Kennungen zu ändern.) Wir sagen , eine Kennung , die ein Element ist R identifiziert R , und dass eine Darstellung , die ein Element ist R steht für R .
Nehmen wir an, R ist ein Hund. Sie identifizieren R zufällig als
/v1/dogs/1
. (Bedeutung/v1/dogs/1
ist ein Mitglied von R. ) Dies ist nur eine von vielen Möglichkeiten, wie Sie R identifizieren können . Sie können R auch als/v1/dogs/1/x-rays
und als identifizieren/v1/rufus
.Wie repräsentieren Sie R ? Vielleicht mit einem Foto. Vielleicht mit einer Reihe von Röntgenstrahlen. Oder vielleicht mit einem Hinweis auf Datum und Uhrzeit, als R zuletzt bellte. Denken Sie jedoch daran, dass dies alles Darstellungen derselben Ressource sind .
/v1/dogs/1/x-rays
ist eine Kennung derselben Ressource, die durch eine Antwort auf die Frage "Wann hat R zuletzt gebellt?" dargestellt wird.HTTP
Mehrere Darstellungen einer Ressource sind nicht sehr nützlich, wenn Sie sich nicht auf die gewünschte beziehen können. Aus diesem Grund ist HTTP nützlich: Sie können Bezeichner mit Darstellungen verbinden . Das heißt, es ist eine Möglichkeit für den Dienst, eine URL zu erhalten und zu entscheiden, welche Darstellung dem Client bereitgestellt werden soll.
Zumindest ist es das, was es
GET
tut.PUT
ist im Grunde das Gegenteil vonGET
: SiePUT
eine Darstellung r an der URL, wenn Sie möchten, dass zukünftigeGET
Anforderungen an diese URL r zurückgeben , mit einigen möglichen Übersetzungen wie JSON in HTML.POST
ist eine lockerere Methode zum Ändern einer Darstellung. Stellen Sie sich vor, es gibt Anzeigelogik und Änderungslogik, die einander entgegengesetzt sind - beide entsprechen derselben URL. Eine POST-Anforderung ist eine Anforderung an die Änderungslogik, die Informationen zu verarbeiten und alle Darstellungen (nicht nur die Darstellung, die sich unter derselben URL befindet) nach eigenem Ermessen zu ändern. Beachten Sie den dritten Absatz nach 9.6 PUT : Sie ersetzen das Objekt an der URL nicht durch neuen Inhalt. Sie bitten die Sache unter der URL, einige Informationen zu verarbeiten und intelligent in Form von informativen Darstellungen zu antworten.In unserem Fall bitten wir die Änderungslogik bei
/v1/dogs/1/bark-schedule
(die das Gegenstück zur Anzeigelogik ist, die uns sagt, wann sie zuletzt gebellt hat und wann sie das nächste Mal bellt), unsere Informationen zu verarbeiten und einige Darstellungen entsprechend zu ändern. In Reaktion auf zukünftigeGET
s sagt uns die Anzeigelogik, die derselben URL entspricht, 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 Dienst dem Kunden mit, wann der Hund das letzte Mal gebellt hat und wann er das nächste Mal gebellt hat. Aus Sicht des Dienstes ist das ehrlich, denn diese Zeiten entsprechen früheren und geplanten Cron-Jobs.
quelle
REST ist ein ressourcenorientierter Standard, es ist nicht handlungsorientiert wie ein RPC.
Wenn Sie möchten, dass Ihr Server bellt , sollten Sie sich mit verschiedenen Ideen wie JSON-RPC oder der Kommunikation mit Websockets befassen.
Jeder Versuch, es RESTful zu halten, schlägt meiner Meinung nach fehl: Sie können einen
POST
mit demaction
Parameter ausgeben , Sie erstellen keine neuen Ressourcen, aber da Sie möglicherweise Nebenwirkungen haben, sind Sie sicherer.quelle
POST
wurde entwickelt, um "einen Datenblock ... für einen Datenverarbeitungsprozess bereitzustellen" . Es scheint, dass viele Leute Ressourcen von Aktionen unterscheiden, aber tatsächlich sind Aktionen nur eine Art von Ressource. Das Aufrufen einer Aktionsressource auf einem Server ist weiterhin eine einheitliche Schnittstelle, die zwischengespeichert, modular und skalierbar ist. Es ist auch zustandslos, aber das kann verletzt werden, wenn der Client eine Antwort erwartet. Das Aufrufen einer "void-Methode" auf dem Server ist jedoch das, was Roy Fielding mit REST beabsichtigt hat .