In diesem Artikel behauptet der Autor, dass
Manchmal ist es erforderlich, eine Operation in der API verfügbar zu machen, die von Natur aus nicht REST-konform ist.
und das
Wenn eine API zu viele Aktionen enthält, ist dies ein Hinweis darauf, dass sie entweder mit einem RPC-Standpunkt entworfen wurde, anstatt RESTful-Prinzipien zu verwenden, oder dass die betreffende API naturgemäß besser für ein RPC-Modell geeignet ist.
Dies spiegelt wider, was ich auch anderswo gelesen und gehört habe.
Ich finde das jedoch ziemlich verwirrend und möchte die Sache besser verstehen.
Beispiel I: Herunterfahren einer VM über eine REST-Schnittstelle
Ich denke, es gibt zwei grundverschiedene Möglichkeiten, ein Herunterfahren einer VM zu modellieren. Jeder Weg mag ein paar Variationen haben, aber konzentrieren wir uns vorerst auf die grundlegendsten Unterschiede.
1. Patchen Sie die state -Eigenschaft der Ressource
PATCH /api/virtualmachines/42
Content-Type:application/json
{ "state": "shutting down" }
(Alternativ PUT
auf der Subressource /api/virtualmachines/42/state
.)
Die VM wird im Hintergrund heruntergefahren und zu einem späteren Zeitpunkt, je nachdem, ob das Herunterfahren erfolgreich ist oder nicht, wird der Status möglicherweise intern mit "Ausschalten" aktualisiert.
2. PUT oder POST für die Eigenschaft actions der Ressource
PUT /api/virtualmachines/42/actions
Content-Type:application/json
{ "type": "shutdown" }
Das Ergebnis ist genau das gleiche wie im ersten Beispiel. Der Status wird so aktualisiert, dass er sofort "herunterfährt" und eventuell "ausschaltet".
Sind beide Designs RESTful?
Welches Design ist besser?
Beispiel II: CQRS
Was ist, wenn wir eine CQRS-Domäne mit vielen solchen "Aktionen" (auch als Befehle bezeichnet) haben, die möglicherweise zu Aktualisierungen mehrerer Aggregate führen oder nicht auf CRUD-Operationen für konkrete Ressourcen und Subressourcen abgebildet werden können?
Sollten wir versuchen, so viele Befehle wie möglich zu modellieren, die mit Concrete erstellt oder auf konkreten Ressourcen aktualisiert werden (gemäß dem ersten Ansatz aus Beispiel I), und im Übrigen "Aktionsendpunkte" verwenden?
Oder sollten wir alle Befehle Aktionsendpunkten zuordnen (wie im zweiten Ansatz von Beispiel I)?
Wo sollen wir die Grenze ziehen? Wann wird das Design weniger RUHIG?
Passt ein CQRS-Modell besser zu einer RPC-ähnlichen API?
Nach dem oben zitierten Text ist es, wie ich es verstehe.
Wie Sie meinen vielen Fragen entnehmen können, bin ich etwas verwirrt über dieses Thema. Können Sie mir helfen, es besser zu verstehen?
quelle
Antworten:
Im ersten Fall (Herunterfahren von VMs) würde ich keine der OP-Alternativen für RESTful halten. Zugegeben, wenn Sie das Richardson-Reifegradmodell als Maßstab verwenden, handelt es sich bei beiden um Level 2- APIs, da sie Ressourcen und Verben verwenden.
Keiner von ihnen, obwohl die Verwendung hypermedia Kontrollen, und meiner Meinung nach , dass die einzige Art von REST ist das unterscheidet RESTful API - Design von RPC. Mit anderen Worten, bleiben Sie bei Level 1 und 2, und Sie werden in den meisten Fällen eine API im RPC-Stil haben.
Um zwei verschiedene Arten des Herunterfahrens einer VM zu modellieren, würde ich die VM selbst als Ressource verfügbar machen, die (unter anderem) Links enthält:
Wenn ein Client das System herunterfahren möchte
Ploeh
VM , muss er der Verknüpfung mit demshut-down
Beziehungstyp folgen . (Normalerweise verwenden Sie, wie im RESTful Web Services-Kochbuch beschrieben , ein IRI oder ein ausführlicheres Identifikationsschema für Beziehungstypen, aber ich habe mich dafür entschieden, das Beispiel so einfach wie möglich zu halten.)In diesem Fall gibt es nur wenige andere Informationen, die für die Aktion erforderlich sind. Der Client sollte daher einfach einen leeren POST für die URL in den
href
folgenden Bereichen erstellen :(Da diese Anforderung keinen Textkörper hat, ist es verlockend, dies als GET-Anforderung zu modellieren. GET-Anforderungen sollten jedoch keine beobachtbaren Nebenwirkungen haben, sodass der POST korrekter ist.)
Wenn ein Client die VM ausschalten möchte, folgt er
power-off
stattdessen dem Link.Mit anderen Worten, die Beziehungstypen der Links bieten Vorteile , die auf Absichten hinweisen. Jeder Beziehungstyp hat eine bestimmte semantische Bedeutung. Aus diesem Grund sprechen wir manchmal über das Semantic Web .
Um das Beispiel so klar wie möglich zu halten, habe ich die URLs in jedem Link absichtlich verdeckt. Wenn der Hosting-Server die eingehende Anforderung empfängt, weiß er, dass
fdaIX
dies Herunterfahren undCHTY91
bedeutet Ausschalten .Normalerweise würde ich die Aktion nur in der URL selbst kodieren, so dass die URLs wären
/vms/1234/shut-down
und/vms/1234/power-off
beim Unterrichten verwischt dies die Unterscheidung zwischen Beziehungstypen (Semantik) und URLs (Implementierungsdetails).Je nachdem, welche Kunden Sie haben, können Sie in Betracht ziehen , RESTful-URLs nicht hackbar zu machen .
CQRS
Wenn es um CQRS geht, sind sich Greg Young und Udi Dahan unter anderem einig, dass CQRS keine Top-Level-Architektur ist . Daher wäre ich vorsichtig, wenn ich eine RESTful-API zu CQRS-ähnlich machen würde, da dies bedeuten würde, dass Clients Teil Ihrer Architektur werden.
Die treibende Kraft hinter einer echten (Level 3) RESTful-API ist häufig, dass Sie Ihre API weiterentwickeln möchten, ohne Clients zu brechen und ohne die Kontrolle über Clients zu haben. Wenn das Ihre Motivation ist, wäre CQRS nicht meine erste Wahl.
quelle
DELETE
mir komisch vor, weil nach dem herunterfahren die vm nur noch im zustand "power off" (oder so) existieren wird.Herunterfahren einer VM über eine REST-Schnittstelle
Dies ist tatsächlich ein ziemlich berühmtes Beispiel, das 2009 von Tim Bray angeführt wurde .
Roy Fielding, der das Problem diskutierte, teilte diese Beobachtung :
Kurz gesagt, Sie haben eine Informationsressource, die eine aktuelle Darstellung des überwachten Zustands zurückgibt. Diese Darstellung kann einen Hypermedien-Link zu einem Formular enthalten , das eine Änderung dieses Status anfordert, und das Formular verfügt über einen weiteren Link zu einer Ressource, die (jede) Änderungsanforderung verarbeitet.
Seth Ladd hatte die wichtigsten Einsichten in das Problem
RESTvolles Programmieren ist Vogons Bürokratie im Webmaßstab. Wie macht man etwas RESTful? Erfinde dafür neue Papiere und digitalisiere die Papiere.
In einer etwas ausgefalleneren Sprache definieren Sie das Domänenanwendungsprotokoll für "Herunterfahren einer VM" und identifizieren die Ressourcen, die Sie zum Anzeigen / Implementieren dieses Protokolls benötigen
Schauen Sie sich Ihre eigenen Beispiele an
Das ist ok; Sie behandeln die Anfrage selbst nicht wirklich als separate Informationsressource, können sie aber dennoch verwalten.
Sie haben ein wenig in Ihrer Darstellung der Änderung verpasst.
Der JSON-Patch- Medientyp formatiert beispielsweise Anweisungen, als ob Sie ein JSON-Dokument direkt ändern würden
In Ihrer Alternative ist die Idee nah, aber nicht offensichtlich richtig.
PUT
ist eine vollständige Ersetzung des Status der Ressource in der Ziel-URL , sodass Sie wahrscheinlich keine Schreibweise wählen würden, die wie eine Sammlung aussieht, als Ziel einer Darstellung einer einzelnen Entität.Stimmt mit der Fiktion überein, dass wir eine Aktion an eine Warteschlange anhängen
Steht im Einklang mit der Fiktion, dass wir das Endelement in der Warteschlange aktualisieren; Es ist ein bisschen komisch, das so zu machen. Das Prinzip der geringsten Überraschung empfiehlt, jedem PUT eine eigene eindeutige Kennung zuzuweisen, anstatt sie alle an einem Ort zu platzieren und mehrere Ressourcen gleichzeitig zu ändern.
Beachten Sie, dass es REST im Hinblick auf die Schreibweise von URI-REST egal ist.
/cc719e3a-c772-48ee-b0e6-09b4e7abbf8b
ist für REST ein perfekter URI. Die Lesbarkeit ist wie bei Variablennamen ein besonderes Anliegen. Die Verwendung von Schreibweisen, die mit RFC 3986 übereinstimmen , macht die Menschen viel glücklicher.CQRS
Greg Young über CQRS
Angesichts der Tatsache, dass Sie im Kontext von HTTP / REST über CQRS sprechen, ist es vernünftig anzunehmen, dass Sie in diesem letzteren Kontext arbeiten. Lassen Sie uns also fortfahren.
Dieser ist überraschenderweise sogar einfacher als Ihr vorheriges Beispiel. Der Grund dafür ist einfach: Befehle sind Nachrichten .
Jim Webber beschreibt HTTP als das Anwendungsprotokoll eines Büros aus den 1950er Jahren. Die Arbeit wird erledigt, indem Nachrichten entgegengenommen und in den Posteingang gestellt werden. Die gleiche Idee gilt - wir erhalten eine leere Kopie eines Formulars, füllen es mit den uns bekannten Einzelheiten aus und liefern es aus. Ta da
Ja, sofern es sich bei den "konkreten Ressourcen" eher um Nachrichten als um Entitäten im Domänenmodell handelt.
Schlüsselidee: Ihre REST-API ist immer noch eine Schnittstelle . Sie sollten in der Lage sein, das zugrunde liegende Modell zu ändern, ohne dass Clients die Nachrichten ändern müssen. Wenn Sie ein neues Modell freigeben, geben Sie eine neue Version Ihrer Web-Endpunkte frei, die wissen, wie Sie Ihr Domänenprotokoll verwenden und auf das neue Modell anwenden.
Nicht wirklich - insbesondere Web-Caches sind ein hervorragendes Beispiel für ein "schließlich konsistentes Lesemodell". Indem Sie jede Ihrer Ansichten unabhängig adressierbar machen und dabei jeweils eigene Caching-Regeln festlegen, erhalten Sie eine Reihe kostenloser Skalierungsfunktionen. Es gibt relativ wenig Anziehungskraft auf einen exklusiven RPC-Ansatz zum Lesen.
Für Schreibvorgänge ist dies eine schwierige Frage: Das Senden aller Befehle an einen einzelnen Handler an einem einzelnen Endpunkt oder an eine einzelne Familie von Endpunkten ist mit Sicherheit einfacher . Bei REST geht es mehr darum, wie Sie feststellen, dass sich der Endpunkt auf dem Client befindet.
Das Behandeln einer Nachricht als eine eigene, eindeutige Ressource hat den Vorteil, dass Sie PUT verwenden können, und macht die zwischengeschalteten Komponenten darauf aufmerksam, dass die Behandlung der Nachricht idempotent ist, sodass sie in bestimmten Fällen der Fehlerbehandlung teilnehmen können . (Hinweis: Wenn die Ressourcen aus Sicht der Clients unterschiedliche URIs aufweisen, handelt es sich um unterschiedliche Ressourcen. Die Tatsache, dass möglicherweise alle denselben Anforderungsbehandlungscode auf dem Ursprungsserver haben, ist ein Implementierungsdetail, das von der Uniform verborgen wird Schnittstelle).
Fielding (2008)
quelle
Sie können 5 Medientypebenen verwenden , um den Befehl im Headerfeld für den Inhaltstyp der Anforderung anzugeben.
In dem VM-Beispiel wäre es etwas in diese Richtung
Dann
Oder
Siehe https://www.infoq.com/articles/rest-api-on-cqrs
quelle
Das Beispiel im verlinkten Artikel basiert auf der Idee, dass das Starten und Herunterfahren der Maschine nicht durch Änderungen des Status der modellierten Ressourcen, sondern durch Befehle gesteuert werden muss. Letzteres ist so ziemlich das, was REST lebt und atmet. Eine bessere Modellierung der VM erfordert einen Blick darauf, wie das reale Gegenstück funktioniert und wie Sie als Mensch damit interagieren würden. Dies ist langwierig, aber ich denke, es gibt einen guten Einblick in die Art des Denkens, die für eine gute Modellierung erforderlich ist.
Es gibt zwei Möglichkeiten, den Energiezustand des Computers auf meinem Schreibtisch zu steuern:
Für eine VM können beide als Lese- / Schreib-Boolesche Werte modelliert werden:
power
- Wenn in geänderttrue
, passiert nichts anderes als eine Notiz, dass der Schalter in diesen Zustand versetzt wurde. Bei der Änderung infalse
wird der VM befohlen, sofort ausgeschaltet zu werden. Der Vollständigkeit halber passiert nichts, wenn der Wert nach einem Schreibvorgang unverändert bleibt.onoff
- Wenn auf geänderttrue
, passiert nichts, wenn dies der Fallpower
istfalse
, wird die VM zum Starten aufgefordert. Wenn auf geändertfalse
, geschieht nichts, wenn dies der Fallpower
istfalse
. Andernfalls wird die VM angewiesen, die Software zu benachrichtigen, damit sie ordnungsgemäß heruntergefahren wird. Anschließend wird die VM darüber informiert, dass sie in den ausgeschalteten Zustand versetzt werden kann. Der Vollständigkeit halber macht ein unverändertes Schreiben nichts.Mit all dem kommt die Erkenntnis, dass es eine Situation gibt, in der der Zustand der Maschine nicht den Zustand der Schalter widerspiegelt, und zwar während des Herunterfahrens.
power
wird immer noch seintrue
undonoff
werdenfalse
, aber der Prozessor wird immer noch heruntergefahren, und dafür müssen wir eine weitere Ressource hinzufügen, damit wir erkennen können, was die Maschine tatsächlich tut:running
- Ein schreibgeschützter Wert, der bestimmt wird,true
wann die VM ausgeführt wird undfalse
wann nicht, indem der Hypervisor nach seinem Status gefragt wird.Das Ergebnis ist, dass Sie sicherstellen müssen, dass
power
undonoff
Ressourcen festgelegt wurden , wenn eine VM gestartet werden solltrue
. (Sie können zulassen, dass derpower
Schritt übersprungen wird, indem Sie ihn selbst zurücksetzen. Wenn Sie ihn auf setzenfalse
, wird ertrue
nach einem überprüfbaren Hard-Stop der VM. Ob dies eine REST-reine Sache ist, ist ein Grund für eine weitere Diskussion.) wenn Sie es wollen , ein ordnungsgemäßes Abschalten tun, legen Sieonoff
auffalse
und kommen später wieder , um zu sehen , ob die Maschine tatsächlich gestoppt, das setzenpower
auf ,false
wenn es nicht tut.Wie in der realen Welt haben Sie immer noch das Problem, angewiesen zu werden, die VM zu starten, nachdem sie auf
onoff
geändert wurde.false
Dies liegt jedochrunning
daran, dass sie gerade heruntergefahren wird. Wie Sie damit umgehen, ist eine politische Entscheidung.quelle
Wenn Sie also in Ruhe nachdenken möchten, vergessen Sie die Befehle. Der Client weist den Server nicht an, die VM herunterzufahren. Der Client "schließt" (bildlich gesprochen) seine Kopie der Ressourcendarstellung, indem er seinen Status aktualisiert und diese Darstellung dann auf den Server zurücksendet. Der Server akzeptiert diese neue Statusdarstellung und fährt als Nebeneffekt die VM herunter. Der Nebenwirkungsaspekt bleibt dem Server überlassen.
Es ist weniger von
Hey Server, Client hier, würde es Ihnen etwas ausmachen, die VM herunterzufahren
und mehr von
Hallo Server, Client hier, ich habe den Status der Ressourcen-VM 42 in den Status "Herunterfahren" geändert, Ihre Kopie dieser Ressource aktualisiert und dann alles getan, was Sie für angemessen halten
Als Nebeneffekt kann der Server, der diesen neuen Status akzeptiert, überprüfen, welche Aktionen er tatsächlich ausführen muss (z. B. das physische Herunterfahren von VM 42), dies ist jedoch für den Client transparent. Der Client ist von allen Aktionen unberührt, die der Server ausführen muss, um mit diesem neuen Status in Einklang zu kommen
Vergiss also die Befehle. Die einzigen Befehle sind die Verben in HTTP für die Statusübertragung. Dem Client ist es egal, wie der Server die physische VM in den Zustand des Herunterfahrens versetzt. Der Client gibt keine Befehle an den Server aus, um dies zu erreichen. Er sagt lediglich, dass dies der neue Status ist .
Dies hat den Vorteil, dass der Client in Bezug auf die Flusskontrolle vom Server entkoppelt wird. Wenn der Server später die Funktionsweise von VMs ändert, ist es dem Client egal. Es sind keine Befehlsendpunkte zum Aktualisieren vorhanden. Wenn Sie in RPC den API-Endpunkt von
shutdown
in ändern , habenshut-down
Sie alle Ihre Clients beschädigt, da sie jetzt den Befehl zum Aufrufen auf dem Server nicht kennen.REST ähnelt der deklarativen Programmierung, bei der Sie anstelle einer Liste mit Anweisungen zur Änderung lediglich angeben, wie Sie dies wünschen, und die Programmierumgebung dies herausfinden lassen.
quelle
POST /api/virtualmachines/42/shutdown
anstatt irgendwelche "Nebenwirkungen" zu haben. Die API muss für den Benutzer verständlich sein. Woher weiß ich, dass beispielsweiseDELETE /api/virtualmachines/42
die VM heruntergefahren wird? Ein Nebeneffekt für mich ist ein Fehler. Wir sollten unsere APIs so gestalten, dass sie verständlich und selbsterklärend sind