Nicht-CRUD-Operationen in einem RESTful-Service

106

Was ist die "RESTful" -Methode, um einem RESTful-Service Nicht-CRUD-Operationen hinzuzufügen? Angenommen, ich habe einen Dienst, der CRUD den Zugriff auf Datensätze wie diesen ermöglicht:

GET /api/car/123           <- Returns information for the Car object with ID 123
POST /api/car              <- Creates a new car (with properties in the request)
PUT /api/car/123           <- Updates car 123 (with properties in the request)
DELETE /api/car/123        <- Deletes car 123    
POST /api/car/123/wheel/   <- Creates a wheel and associates it to car 123

Wenn ich die Farbe des Autos ändern möchte, würde ich einfach POST /api/car/123eine POST-Variable für die neue Farbe einfügen.

Angenommen, ich möchte ein Auto kaufen, und dieser Vorgang ist komplizierter als das einfache Aktualisieren der Eigenschaft "Eigenes Auto" eines "Benutzer" -Datensatzes. Ist es RESTful, einfach so etwas zu tun POST /api/car/123/purchase, bei dem "Kauf" im Wesentlichen ein Methodenname ist? Oder sollte ich ein benutzerdefiniertes HTTP-Verb verwenden, wie PURCHASEanstelle von POST?

Oder liegen Nicht-CRUD-Operationen vollständig außerhalb des Bereichs von REST?

MikeWyatt
quelle
5
Wenn Sie die Farbe eines Autos ändern, ist es besser PATCH /api/car/123, einen Farbparameter zu verwenden PUT /api/car/123und zu senden ODER das gesamte Autoobjekt zu verwenden und zu senden. POST würde schließen, dass Sie ein neues Auto erstellen und sollte wahrscheinlich nie eine ID am Ende der URL
RonnyKnoxville

Antworten:

65

Stellen Sie sich den Kauf als Geschäftseinheit oder Ressource im RESTful-Wörterbuch vor. Abgesehen davon schafft ein Kauf tatsächlich eine neue Ressource. So:

POST /api/purchase

wird eine neue Bestellung aufgeben. Die Details (Benutzer, Auto usw.) sollten durch die ID (oder URI) in den an diese Adresse gesendeten Inhalten angegeben werden.

Es spielt keine Rolle, dass die Bestellung eines Autos nicht nur ein einfaches EINFÜGEN in die Datenbank ist. Bei REST geht es eigentlich nicht darum, Ihre Datenbanktabellen als CRUD-Operationen verfügbar zu machen. Aus logischer Sicht erstellen Sie eine Bestellung (Kauf), aber die Serverseite kann so viele Verarbeitungsschritte ausführen, wie sie möchte.

Sie können das HTTP-Protokoll sogar noch weiter missbrauchen. Verwenden Sie den LocationHeader, um einen Link zu einer neu erstellten Bestellung zurückzugeben, wählen Sie sorgfältig die HTTP-Antwortcodes aus, um Benutzer über Probleme (server- oder clientseitig) usw. zu informieren.

Tomasz Nurkiewicz
quelle
3
Bei REST geht es darum, den Status von Ressourcen zu manipulieren, und jeder Geschäftsvorgang muss dem Status von CRUD-Vorgängen zugeordnet werden. Wenn Sie eine Semantik für harte Geschäftsvorgänge benötigen , müssen Sie den SOAP-Weg gehen (SOAP ist eigentlich die Nachrichtenübermittlung, wird jedoch normalerweise in Anforderungs-Antwort-Vorgängen organisiert).
Tomasz Nurkiewicz
23
Das Design "Kauf als Ressource" sieht ordentlich aus. Was ist, wenn die Ressource ein "Bier" ist ... und ich möchte, dass der Server es trinkt ... (es war für mich, ich würde es sicherlich ERHALTEN;)) .. sollten wir die "Trinkaktion" als Ressource betrachten ? .. .. oder ist "ein Bier trinken" ein harter Geschäftsbetrieb ?! Im Ernst, ist das RESTful Design über die Betrachtung von Aktionen als Ressourcen ?! ..
Myobis
2
Wie würden Sie "Bestellung genehmigen" über einen REST-Service verfügbar machen? Ich denke, @TomaszNurkiewicz hat insofern Recht, als alles, was nicht sauber auf CRUD-Weise gemacht werden kann, die von SOAP bereitgestellte Operationssemantik benötigt. Es sei denn, "Bestellgenehmigung" ist ein eigenständiges Modell / Unternehmen. ZB POST / Po-Genehmigung (mit PO-Details in der Anfrage).
Mydoghaswürmer
2
Aus Sicht eines REST-Kunden sollte "Bestellung genehmigen" nur eine weitere Aktualisierung der Bestellung sein. Ändern Sie beispielsweise "Genehmigt" in "Wahr" und senden Sie das Update an den Server. Der Server muss wahrscheinlich eine Reihe von Überprüfungen durchführen und wahrscheinlich eine Reihe anderer Ressourcen aktualisieren / erstellen. Aber das ist das Serverproblem und sollte für den Client nicht sichtbar sein.
AVee
2
@antinome: "Angenommen, der Client weiß etwas davon", wenn dies der Fall ist, machen Sie kein REST (es könnte jedoch immer noch eine gültige, sinnvolle Software sein!). REST wurde entwickelt, um Clients erstellen zu können, die so etwas nicht wissen, und um Clients zu erstellen, die noch funktionieren, wenn sich das Verhalten des Servers ändert. Was Sie versuchen, ist klassisches RPC. Sie müssen entweder Ihren Ansatz überprüfen, damit er zu REST passt, oder Sie müssen akzeptieren, dass Sie RPC ausführen und ein für RPC bestimmtes Protokoll wie SOAP verwenden. REST ist sehr bemüht, kein RPC zu sein, daher wird es nie gut passen, wenn Sie RPC wollen / brauchen.
AVee
15

Der RESTful-Weg, wie ich es verstehe, ist, dass Sie keine neuen HTTP-Verben benötigen. Irgendwo gibt es ein Substantiv, das bedeutet, was Sie tun müssen.

Ein Auto kaufen? Ist das nicht so?

POST /api/order
djna
quelle
2
Wird PUT nicht zum Aktualisieren von Ressourcen verwendet, da es idempotent ist? Dies bedeutet, dass Sie es so oft anrufen können, wie Sie möchten, aber nur der erste / letzte Anruf ist wichtig. POST hingegen wird zum Erstellen von Ressourcen verwendet, und durch zweimaliges Aufrufen sollten tatsächlich zwei erstellt werden.
Tomasz Nurkiewicz
1
@Tomas, ja, Tippfehler. Das Prinzip ist jedoch wichtig, wir haben es mit einer neuen Sache zu tun, einer Ordnung, ohne dass ein neues Verb benötigt wird.
DJNA
5

Was Sie wirklich tun, ist eine Bestellung zu erstellen. Fügen Sie also eine weitere Ressource für Bestellung und Post hinzu und legen Sie diese während des Bestellvorgangs dort ab.

Denken Sie eher an Ressourcen als an Methodenaufrufe.

Um die Bestellung abzuschließen, würden Sie wahrscheinlich POST / api / order // complete oder ähnliches.

Andrew Kothmann
quelle
3

Ich bin der Meinung, dass REST-APIs viel mehr helfen als nur Semantik bereitzustellen. Daher kann der RPC-Stil nicht ausgewählt werden, nur weil einige Aufrufe im RPC-Betriebsstil sinnvoller erscheinen. Beispiel ist die Google Maps-API, um Wegbeschreibungen zwischen zwei Orten zu finden. Sieht folgendermaßen aus: http://maps.googleapis.com/maps/api/directions/json?origin=Jakkur&destination=Hebbal

Sie hätten es "findDirections" (Verb) nennen und es als Operation behandeln können. Vielmehr machten sie "Richtung" (Substantiv) als Ressource und behandelten das Finden von Richtungen als eine Abfrage in der Richtungsressource (obwohl es intern keine echte Ressource namens Richtung geben konnte und es von der Geschäftslogik implementiert werden konnte, Richtungen basierend auf Parametern zu finden).

Maruthi
quelle
Das ist ein schlechtes Beispiel. In diesem Fall sind die Richtungen (alle möglichen Richtungen, unendlich viele) die Ressource und die Parameter sind nur Filter. Aber Sie können damit keinen "Kauf" tätigen, da Filter nur dann sinnvoll sind, wenn Vorgänge abgerufen und eine Bestellung aufgegeben oder storniert werden. Dies sind Vorgänge, die die Daten ändern
Tseng
2
Kauf wäre POST to / order mit einem json im body, um anzuzeigen, dass eine bestellung erstellt wurde. Abbrechen wäre PUT to / order mit einem JSON, der die Änderung des Bestellstatus trägt, um anzuzeigen, dass es sich um ein idempotentes Update handelt. Ich muss noch auf eine Operation stoßen, die nicht in einem Ressourcenformat ausgedrückt werden kann. Ich würde mich sehr über ein
solches