Transaktionen in REST?

147

Ich frage mich, wie Sie den folgenden Anwendungsfall in REST implementieren würden. Ist es überhaupt möglich, auf das konzeptionelle Modell zu verzichten?

Lesen oder aktualisieren Sie mehrere Ressourcen im Rahmen einer einzelnen Transaktion. Überweisen Sie beispielsweise 100 USD von Bobs Bankkonto auf Johns Konto.

Soweit ich das beurteilen kann, ist die einzige Möglichkeit, dies umzusetzen, das Betrügen. Sie können auf die mit John oder Bob verknüpfte Ressource POSTEN und den gesamten Vorgang mit einer einzigen Transaktion ausführen. Für mich bricht dies die REST-Architektur, da Sie im Wesentlichen einen RPC-Aufruf über POST tunneln, anstatt wirklich mit einzelnen Ressourcen zu arbeiten.

Gili
quelle

Antworten:

91

Stellen Sie sich ein RESTful-Warenkorbszenario vor. Der Warenkorb ist konzeptionell Ihr Transaktions-Wrapper. Auf die gleiche Weise, wie Sie einem Warenkorb mehrere Artikel hinzufügen und diesen dann zur Bearbeitung der Bestellung senden können, können Sie Bobs Kontoeintrag zum Transaktions-Wrapper und dann Bills Kontoeintrag zum Wrapper hinzufügen. Wenn alle Teile vorhanden sind, können Sie den Transaktions-Wrapper mit allen Komponenten POST / PUT.

Darrel Miller
quelle
18
Warum sollte TransferMoneyTransaction keine tragfähige Bankressource sein?
Darrel Miller
8
Wenn Sie sicherstellen, dass sich Ihre Endpunkte auf Substantive beziehen, ist es normalerweise intuitiv, was die Standardverben GET, PUT, POST, DELETE mit diesem Substantiv tun. Mit RPC können Endpunkte selbst Verben sein, und daher können sie mit den HTTP-Verben in Konflikt stehen, und die Absicht wird verwirrend.
Darrel Miller
10
Beispiel: Was passiert, wenn Sie auf dem Endpunkt UpdateXYZ ein HTTP-LÖSCHEN ausführen? Löscht es XYZ? Löscht es das Update oder führt es nur ein Update durch und ignoriert das HTTP-Verb delete. Indem Sie Verben vom Endpunkt fernhalten, beseitigen Sie die Verwirrung.
Darrel Miller
5
Und was ist mit Transaktionen über mehrere Dienste hinweg? und was ist, wenn Sie eine Reihe von "nicht verwandten" Änderungen vornehmen möchten, für die der Service keinen impliziten Transaktionscontainer bereitstellt. Außerdem, warum sollten Sie einen bestimmten Transaktionstyp haben, wenn wir zu Allzwecktransaktionen wechseln, die völlig unabhängig von Ihren tatsächlichen Daten sind Änderungen. Transaktionen stimmen möglicherweise nicht mit Restful überein, aber es scheint, dass Transaktionen übereinander geschichtet werden sollten, unabhängig von den restlichen Aufrufen, abgesehen von der Tatsache, dass die Anforderungsheader eine Transaktionsreferenz enthalten würden.
Meandmycode
4
@meandmycode Datenbanktransaktionen sollten hinter einer REST-Schnittstelle geschichtet werden. Alternativ können Sie eine Geschäftstransaktion (keine Datenbanktransaktion) als Ressource für sich verfügbar machen und dann im Fehlerfall Ausgleichsmaßnahmen ergreifen.
Darrel Miller
60

Es gibt einige wichtige Fälle, die von dieser Frage nicht beantwortet werden, was ich für schade halte, da sie bei Google einen hohen Rang für die Suchbegriffe hat :-)

Konkret wäre eine nette Eigenschaft: Wenn Sie zweimal POSTEN (weil ein Cache in der Zwischenzeit einen Schluckauf hat), sollten Sie den Betrag nicht zweimal überweisen.

Um dies zu erreichen, erstellen Sie eine Transaktion als Objekt. Dies könnte alle Daten enthalten, die Sie bereits kennen, und die Transaktion in einen ausstehenden Zustand versetzen.

POST /transfer/txn
{"source":"john's account", "destination":"bob's account", "amount":10}

{"id":"/transfer/txn/12345", "state":"pending", "source":...}

Sobald Sie diese Transaktion abgeschlossen haben, können Sie sie wie folgt festschreiben:

PUT /transfer/txn/12345
{"id":"/transfer/txn/12345", "state":"committed", ...}

{"id":"/transfer/txn/12345", "state":"committed", ...}

Beachten Sie, dass mehrere Puts an dieser Stelle keine Rolle spielen. Sogar ein GET auf dem TXN würde den aktuellen Status zurückgeben. Insbesondere erkennt der zweite PUT, dass sich der erste bereits im entsprechenden Status befindet, und gibt ihn einfach zurück. Wenn Sie versuchen, ihn in den Status "Rollback" zu versetzen, nachdem er sich bereits im Status "Festgeschrieben" befindet, erhalten Sie einen Fehler und die tatsächlich festgeschriebene Transaktion zurück.

Solange Sie mit einer einzelnen Datenbank oder einer Datenbank mit integriertem Transaktionsmonitor sprechen, funktioniert dieser Mechanismus tatsächlich einwandfrei. Sie können zusätzlich Zeitüberschreitungen für Transaktionen einführen, die Sie sogar mit Expires-Headern ausdrücken können, wenn Sie möchten.

Jon Watte
quelle
Interessante Diskussion! Ich möchte hinzufügen, dass der erste Beitrag in einem Schritt ausgeführt werden muss. Es kann nicht später hinzugefügt werden (dann befinden wir uns im Warenkorbgebiet und Einkaufswagen verfügen über viele Checks and Balances, um zu verhindern, dass sie dem Endbenutzer Schaden zufügen, selbst die Gesetzgebung, Banküberweisungen nicht) ...
Erk
33

In REST-Begriffen sind Ressourcen Substantive, auf die mit CRUD-Verben (Erstellen / Lesen / Aktualisieren / Löschen) reagiert werden kann. Da es kein Verb "Geld überweisen" gibt, müssen wir eine "Transaktions" -Ressource definieren, auf die mit CRUD reagiert werden kann. Hier ist ein Beispiel in HTTP + POX. Der erste Schritt besteht darin, eine neue leere Transaktion zu erstellen (HTTP-POST-Methode) :

POST /transaction

Dies gibt eine Transaktions-ID zurück, z. B. "1234" und die entsprechende URL "/ transaction / 1234". Beachten Sie, dass durch mehrmaliges Auslösen dieses POST nicht dieselbe Transaktion mit mehreren IDs erstellt wird und auch die Einführung eines "ausstehenden" Status vermieden wird. Außerdem kann POST nicht immer idempotent sein (eine REST-Anforderung). Daher ist es im Allgemeinen empfehlenswert, Daten in POSTs zu minimieren.

Sie können die Generierung einer Transaktions-ID dem Client überlassen. In diesem Fall würden Sie POST / transaction / 1234, um die Transaktion "1234" zu erstellen, und der Server würde einen Fehler zurückgeben, wenn dieser bereits vorhanden ist. In der Fehlerantwort könnte der Server eine derzeit nicht verwendete ID mit einer entsprechenden URL zurückgeben. Es ist keine gute Idee, den Server mit einer GET-Methode nach einer neuen ID abzufragen, da GET den Serverstatus niemals ändern sollte und das Erstellen / Reservieren einer neuen ID den Serverstatus ändern würde.

Als nächstes aktualisieren wir die Transaktion mit allen Daten (PUT HTTP-Methode) und legen sie implizit fest:

PUT /transaction/1234
<transaction>
  <from>/account/john</from>
  <to>/account/bob</to>
  <amount>100</amount>
</transaction>

Wenn eine Transaktion mit der ID "1234" zuvor PUT war, gibt der Server eine Fehlerantwort aus, andernfalls eine OK-Antwort und eine URL, um die abgeschlossene Transaktion anzuzeigen.

NB: In / account / john sollte "john" wirklich Johns eindeutige Kontonummer sein.

Tuckster
quelle
4
Das Gleichsetzen von REST mit CRUD ist ein schwerwiegender Fehler. POST muss nicht CREATE bedeuten.
12
Schwerer Fehler? Ich weiß, dass es Unterschiede zwischen PUT und POST gibt, aber es gibt eine lose Zuordnung zu CRUD. "Ernsthaft"?
Ted Johnson
3
Ja im Ernst. CRUD ist eine Methode zur Strukturierung der Datenspeicherung. REST ist eine Möglichkeit zur Strukturierung des Anwendungsdatenflusses. Sie können CRUD auf REST ausführen, aber Sie können REST nicht auf CRUD ausführen. Sie sind nicht gleichwertig.
Jon Watte
20

Gute Frage, REST wird meistens mit datenbankähnlichen Beispielen erklärt, in denen etwas gespeichert, aktualisiert, abgerufen, gelöscht wird. Es gibt nur wenige Beispiele wie dieses, bei denen der Server die Daten auf irgendeine Weise verarbeiten soll. Ich glaube nicht, dass Roy Fielding irgendetwas in seine These aufgenommen hat, die schließlich auf http basierte.

Aber er spricht von "repräsentativem Zustandstransfer" als Zustandsmaschine, wobei Verbindungen zum nächsten Zustand übergehen. Auf diese Weise verfolgen die Dokumente (die Darstellungen) den Clientstatus, anstatt dass der Server dies tun muss. Auf diese Weise gibt es keinen Client-Status, sondern nur den Status, auf dem Sie sich befinden.

Ich habe darüber nachgedacht, und es erscheint mir vernünftig, dass der Server beim Hochladen automatisch verwandte Ressourcen erstellt und Ihnen die Links zu diesen gibt, um den Server dazu zu bringen, etwas für Sie zu verarbeiten (tatsächlich würde dies nicht der Fall sein) Sie müssen nicht automatisch erstellt werden: Sie können nur die Links anzeigen und sie nur erstellen, wenn und wenn Sie ihnen folgen (verzögerte Erstellung). Und um Ihnen auch Links zum Erstellen neuer verwandter Ressourcen zu geben - eine verwandte Ressource hat denselben URI, ist jedoch länger (fügt ein Suffix hinzu). Beispielsweise:

  1. Sie laden die Darstellung des Transaktionskonzepts mit allen Informationen hoch ( POST ) . Dies sieht genauso aus wie ein RPC-Aufruf, erstellt jedoch wirklich die "vorgeschlagene Transaktionsressource". zB URI: Durch /transaction Störungen werden mehrere solcher Ressourcen mit jeweils einer anderen URI erstellt.
  2. In der Antwort des Servers wird der URI der erstellten Ressource sowie deren Darstellung angegeben. Dazu gehört der Link ( URI ) zum Erstellen der zugehörigen Ressource einer neuen "festgeschriebenen Transaktionsressource". Andere verwandte Ressourcen sind der Link zum Löschen der vorgeschlagenen Transaktion. Dies sind Zustände in der Zustandsmaschine, denen der Client folgen kann. Logischerweise sind diese Teile der Ressource, die auf dem Server erstellt wurde, über die vom Client bereitgestellten Informationen hinaus. zB URIs : /transaction/1234/proposed, /transaction/1234/committed
  3. Sie POSTEN auf den Link, um die "festgeschriebene Transaktionsressource" zu erstellen , die diese Ressource erstellt und den Status des Servers (die Salden der beiden Konten) ändert **. Diese Ressource kann naturgemäß nur einmal erstellt und nicht aktualisiert werden. Daher können keine Störungen auftreten, die viele Transaktionen begehen.
  4. Sie können diese beiden Ressourcen abrufen, um zu sehen, wie ihr Status ist. Unter der Annahme, dass ein POST andere Ressourcen ändern kann, wird der Vorschlag jetzt als "festgeschrieben" gekennzeichnet (oder möglicherweise überhaupt nicht verfügbar).

Dies ähnelt der Funktionsweise von Webseiten. Auf der letzten Webseite steht "Sind Sie sicher, dass Sie dies tun möchten?". Diese endgültige Webseite ist selbst eine Darstellung des Status der Transaktion, die einen Link zum nächsten Status enthält. Nicht nur Finanztransaktionen; auch (zB) Vorschau dann auf Wikipedia festschreiben. Ich denke, der Unterschied in REST besteht darin, dass jede Stufe in der Folge von Zuständen einen expliziten Namen (ihren URI) hat.

Bei realen Transaktionen / Verkäufen gibt es häufig unterschiedliche physische Dokumente für verschiedene Phasen einer Transaktion (Angebot, Bestellung, Quittung usw.). Noch mehr für den Kauf eines Hauses, mit Siedlung etc.

OTOH Das fühlt sich für mich an, als würde ich mit Semantik spielen. Ich bin unzufrieden mit der Nominalisierung der Konvertierung von Verben in Substantive, um sie RESTful zu machen, "weil Substantive (URIs) anstelle von Verben (RPC-Aufrufen) verwendet werden". dh das Substantiv "festgeschriebene Transaktionsressource" anstelle des Verbs "diese Transaktion festschreiben". Ich denke, ein Vorteil der Nominalisierung besteht darin, dass Sie die Ressource nach Namen referenzieren können, anstatt sie auf andere Weise angeben zu müssen (z. B. um den Sitzungsstatus beizubehalten, damit Sie wissen, was "diese" Transaktion ist ...).

Die wichtige Frage ist jedoch: Was sind die Vorteile dieses Ansatzes? dh Inwiefern ist dieser REST-Stil besser als der RPC-Stil? Ist eine Technik, die sich hervorragend für Webseiten eignet, auch für die Verarbeitung von Informationen hilfreich, die über das Speichern / Abrufen / Aktualisieren / Löschen hinausgehen? Ich denke, dass der Hauptvorteil von REST die Skalierbarkeit ist. Ein Aspekt davon ist, dass der Client-Status nicht explizit beibehalten werden muss (sondern dass er implizit im URI der Ressource und im nächsten Status als Links in ihrer Darstellung enthalten sein muss). In diesem Sinne hilft es. Vielleicht hilft das auch beim Layering / Pipelining? OTOH nur der eine Benutzer wird sich seine spezifische Transaktion ansehen, daher ist es nicht vorteilhaft, sie zwischenzuspeichern, damit andere sie lesen können, der große Gewinn für http.

13ren
quelle
Könnten Sie bitte erklären, wie "die Notwendigkeit, den Status auf dem Client nicht beizubehalten" die Skalierbarkeit verbessert? Welche Skalierbarkeit? Skalierbarkeit in welchem ​​Sinne?
Jhegedus
11

Wenn Sie sich zurückziehen, um die Diskussion hier zusammenzufassen, ist es ziemlich klar, dass REST für viele APIs nicht geeignet ist, insbesondere wenn die Client-Server-Interaktion von Natur aus statusbehaftet ist, wie dies bei nicht trivialen Transaktionen der Fall ist. Warum sollten Sie für Client und Server durch alle vorgeschlagenen Rahmen springen, um pedantisch einem Prinzip zu folgen, das nicht zum Problem passt? Ein besseres Prinzip besteht darin, dem Kunden die einfachste, natürlichste und produktivste Möglichkeit zu geben, mit der Anwendung zu komponieren.

Zusammenfassend lässt sich sagen, dass Sie keine RESTful-API erstellen sollten, wenn Sie wirklich viele Transaktionen (Typen, keine Instanzen) in Ihrer Anwendung ausführen.

Peris
quelle
9
Richtig, aber was sollte eine Alternative bei einer verteilten Mikrodienstarchitektur sein?
Vitamon
11

Ich habe mich 10 Jahre lang von diesem Thema entfernt. Wenn ich zurückkomme, kann ich nicht glauben, dass sich die Religion als Wissenschaft tarnt, in die Sie sich stürzen, wenn Sie google rest + zuverlässig sind. Die Verwirrung ist mythisch.

Ich würde diese allgemeine Frage in drei Teile teilen:

  • Nachgelagerte Dienste. Jeder von Ihnen entwickelte Webdienst verfügt über nachgelagerte Dienste, die Sie verwenden und deren Transaktionssyntax Sie nur befolgen müssen. Sie sollten versuchen, all dies vor den Benutzern Ihres Dienstes zu verbergen, sicherstellen, dass alle Teile Ihres Vorgangs als Gruppe erfolgreich sind oder fehlschlagen, und dieses Ergebnis dann an Ihre Benutzer zurückgeben.
  • Ihre Dienste. Kunden wünschen sich eindeutige Ergebnisse für Web-Service-Anrufe, und das übliche REST-Muster, POST-, PUT- oder DELETE-Anfragen direkt an inhaltliche Ressourcen zu richten, scheint mir eine schlechte und leicht zu verbessernde Methode zu sein, um diese Sicherheit zu gewährleisten. Wenn Sie Wert auf Zuverlässigkeit legen, müssen Sie Aktionsanforderungen identifizieren. Diese ID kann eine auf dem Client erstellte Guid oder ein Startwert aus einer relationalen Datenbank auf dem Server sein. Dies spielt keine Rolle. Verwenden Sie für vom Server generierte IDs eine "Preflight" -Anforderungsantwort, um die ID der Aktion auszutauschen. Wenn diese Anforderung fehlschlägt oder die Hälfte erfolgreich ist, kein Problem, wiederholt der Client die Anforderung einfach. Nicht verwendete IDs schaden nicht.

    Dies ist wichtig, da dadurch alle nachfolgenden Anforderungen vollständig idempotent sind, in dem Sinne, dass sie, wenn sie n-mal wiederholt werden, dasselbe Ergebnis zurückgeben und nichts weiter bewirken. Der Server speichert alle Antworten anhand der Aktions-ID. Wenn dieselbe Anforderung angezeigt wird, wird dieselbe Antwort erneut abgespielt. Eine ausführlichere Beschreibung des Musters finden Sie in diesem Google-Dokument . Das Dokument schlägt eine Implementierung vor, die meines Erachtens (!) Im Großen und Ganzen den REST-Prinzipien folgt. Experten werden mir sicherlich sagen, wie es andere verletzt. Dieses Muster kann für jeden unsicheren Aufruf Ihres Webdienstes verwendet werden, unabhängig davon, ob es sich um nachgelagerte Transaktionen handelt oder nicht.
  • Integration Ihres Dienstes in "Transaktionen", die von vorgelagerten Diensten gesteuert werden. Im Zusammenhang mit Webdiensten werden vollständige ACID-Transaktionen als in der Regel nicht lohnenswert angesehen. Sie können jedoch den Verbrauchern Ihres Dienstes erheblich helfen, indem Sie in Ihrer Bestätigungsantwort Links zum Abbrechen und / oder Bestätigen bereitstellen und so Transaktionen durch Entschädigung erzielen .

Ihre Anforderung ist grundlegend. Lassen Sie sich nicht sagen, dass Ihre Lösung nicht koscher ist. Beurteilen Sie ihre Architekturen anhand dessen, wie gut und wie einfach sie Ihr Problem angehen.

bbsimonbb
quelle
9

Sie müssten Ihre eigene "Transaktions-ID" -Typ-Verwaltung durchführen. Es wären also 4 Anrufe:

http://service/transaction (some sort of tx request)
http://service/bankaccount/bob (give tx id)
http://service/bankaccount/john (give tx id)
http://service/transaction (request to commit)

Sie müssten die Speicherung der Aktionen in einer Datenbank (bei Lastenausgleich) oder im Speicher oder dergleichen durchführen und dann Commit, Rollback und Timeout durchführen.

Kein wirklich ruhiger Tag im Park.

TheSoftwareJedi
quelle
4
Ich denke nicht, dass dies eine besonders gute Illustration ist. Sie benötigen nur zwei Schritte: Transaktion erstellen (erstellt eine Transaktion im Status "Ausstehend") und Transaktion festschreiben (Festschreiben, wenn nicht festgeschrieben, und Versetzen der Ressource in den Status Festgeschrieben oder Zurückgesetzt).
Jon Watte
2

Ich denke, dass es in diesem Fall völlig akzeptabel ist, die reine Theorie von REST in dieser Situation zu brechen. Auf jeden Fall glaube ich nicht, dass es in REST tatsächlich etwas gibt, das besagt, dass Sie abhängige Objekte in Geschäftsfällen, die dies erfordern, nicht berühren können.

Ich denke wirklich, dass es die zusätzlichen Rahmen nicht wert ist, durch die Sie springen würden, um einen benutzerdefinierten Transaktionsmanager zu erstellen, wenn Sie einfach die Datenbank dafür nutzen könnten.

Toby Hede
quelle
2

Zuallererst ist das Überweisen von Geld nichts, was Sie nicht in einem einzigen Ressourcenaufruf tun können. Die Aktion, die Sie ausführen möchten, ist das Senden von Geld. Sie fügen dem Konto des Absenders eine Geldtransferressource hinzu.

POST: accounts/alice, new Transfer {target:"BOB", abmount:100, currency:"CHF"}.

Getan. Sie müssen nicht wissen, dass dies eine Transaktion ist, die atomar sein muss usw. Sie überweisen einfach Geld aka. Senden Sie Geld von A nach B.


Aber für die seltenen Fälle hier eine allgemeine Lösung:

Wenn Sie etwas sehr Komplexes tun möchten, das viele Ressourcen in einem definierten Kontext mit vielen Einschränkungen umfasst, die tatsächlich die Was-gegen-Warum-Barriere (Geschäfts- oder Implementierungswissen) überschreiten, müssen Sie den Status übertragen. Da REST zustandslos sein sollte, müssen Sie als Client den Status übertragen.

Wenn Sie den Status übertragen, müssen Sie die darin enthaltenen Informationen vor dem Client verbergen. Der Kunde sollte keine internen Informationen kennen, die nur für die Implementierung benötigt werden, aber keine geschäftsrelevanten Informationen enthalten. Wenn diese Informationen keinen geschäftlichen Wert haben, sollte der Status verschlüsselt und eine Metapher wie Token, Pass oder etwas verwendet werden.

Auf diese Weise kann man den internen Status weitergeben und mithilfe von Verschlüsselung und Signatur kann das System immer noch sicher und solide sein. Die richtige Abstraktion für den Kunden zu finden, warum er Statusinformationen weitergibt, hängt vom Design und der Architektur ab.


Die wirkliche Lösung:

Denken Sie daran, dass REST über HTTP spricht und HTTP mit dem Konzept der Verwendung von Cookies geliefert wird. Diese Cookies werden häufig vergessen, wenn über die REST-API sowie über Workflows und Interaktionen gesprochen wird, die sich über mehrere Ressourcen oder Anforderungen erstrecken.

Denken Sie daran, was in der Wikipedia über HTTP-Cookies geschrieben steht:

Cookies wurden als zuverlässiger Mechanismus für Websites entwickelt, um sich an wichtige Informationen (z. B. Artikel in einem Warenkorb) zu erinnern oder die Browsing-Aktivitäten des Benutzers aufzuzeichnen (einschließlich Klicken auf bestimmte Schaltflächen, Anmelden oder Aufzeichnen, welche Seiten der Benutzer bisher besucht hat zurück wie vor Monaten oder Jahren).

Wenn Sie also den Status weitergeben müssen, verwenden Sie ein Cookie. Es wurde aus genau dem gleichen Grund entwickelt, es ist HTTP und daher von Natur aus mit REST kompatibel :).


Die bessere Lösung:

Wenn Sie über einen Client sprechen, der einen Workflow mit mehreren Anforderungen ausführt, sprechen Sie normalerweise über das Protokoll. Jede Form von Protokoll enthält eine Reihe von Voraussetzungen für jeden möglichen Schritt, z. B. Schritt A ausführen, bevor Sie B ausführen können.

Dies ist natürlich, aber das Aussetzen des Protokolls für Clients macht alles komplexer. Um dies zu vermeiden, denken Sie einfach darüber nach, was wir tun, wenn wir komplexe Interaktionen und Dinge in der realen Welt ausführen müssen. Wir benutzen einen Agenten.

Mithilfe der Agentenmetapher können Sie eine Ressource bereitstellen, die alle erforderlichen Schritte für Sie ausführen und die tatsächliche Zuweisung / Anweisung, auf die sie reagiert, in ihrer Liste speichern kann (sodass wir POST für den Agenten oder eine 'Agentur' verwenden können).

Ein komplexes Beispiel:

Ein Haus kaufen:

Sie müssen Ihre Glaubwürdigkeit nachweisen (z. B. die Bereitstellung Ihrer Polizeiaufzeichnungen), Sie müssen finanzielle Details sicherstellen, Sie müssen das eigentliche Haus mit einem Anwalt und einem vertrauenswürdigen Dritten kaufen, der die Gelder aufbewahrt, überprüfen, ob das Haus Ihnen gehört und Fügen Sie das Kaufmaterial Ihren Steuerunterlagen usw. hinzu (nur als Beispiel können einige Schritte falsch sein oder was auch immer).

Diese Schritte können mehrere Tage dauern, einige können parallel ausgeführt werden usw.

Um dies zu tun, geben Sie dem Agenten einfach die Aufgabe Haus kaufen wie folgt:

POST: agency.com/ { task: "buy house", target:"link:toHouse", credibilities:"IamMe"}.

Getan. Die Agentur sendet Ihnen eine Referenz zurück, mit der Sie den Status dieses Auftrags anzeigen und verfolgen können. Der Rest wird automatisch von den Agenten der Agentur erledigt.

Denken Sie zum Beispiel an einen Bug-Tracker. Grundsätzlich melden Sie den Fehler und können anhand der Fehler-ID überprüfen, was los ist. Sie können sogar einen Dienst verwenden, um Änderungen dieser Ressource abzuhören. Mission erledigt.

Martin Kersten
quelle
1

Sie dürfen in REST keine serverseitigen Transaktionen verwenden.

Eine der REST-Einschränkungen:

Staatenlos

Die Client-Server-Kommunikation wird weiter eingeschränkt, da zwischen den Anforderungen kein Client-Kontext auf dem Server gespeichert wird. Jede Anforderung von einem Client enthält alle Informationen, die zum Bearbeiten der Anforderung erforderlich sind, und jeder Sitzungsstatus wird im Client gespeichert.

Die einzige RESTful-Methode besteht darin, ein Transaktions-Redo-Protokoll zu erstellen und es in den Client-Status zu versetzen. Mit den Anforderungen sendet der Client das Redo-Protokoll und der Server wiederholt die Transaktion und

  1. rollt die Transaktion zurück, stellt jedoch ein neues Transaktionswiederholungsprotokoll bereit (einen Schritt weiter)
  2. oder schließen Sie die Transaktion ab.

Aber vielleicht ist es einfacher, eine auf Serversitzungen basierende Technologie zu verwenden, die serverseitige Transaktionen unterstützt.

bebbo
quelle
Das Zitat stammt aus dem Wikipedia REST-Eintrag. Ist das die wahre Quelle oder hat Wikipedia sie von irgendwoher bekommen? Wer sagt, was ist Client-Kontext und was Server-Kontext?
Bbsimonbb
1

Ich glaube, dass dies der Fall wäre, wenn eine eindeutige Kennung verwendet würde, die auf dem Client generiert wird, um sicherzustellen, dass der Verbindungsfehler nicht zu einer von der API gespeicherten Duplizität führt.

Ich denke, die Verwendung eines vom Client generierten GUID-Felds zusammen mit dem Überweisungsobjekt und die Sicherstellung, dass dieselbe GUID nicht erneut eingefügt wurde, wäre eine einfachere Lösung für die Überweisungssache.

Sie kennen keine komplexeren Szenarien wie die Buchung mehrerer Flugtickets oder Mikroarchitekturen.

Ich fand ein Papier zu diesem Thema, in dem die Erfahrungen im Umgang mit der Transaktionsatomarität in RESTful-Diensten beschrieben wurden .

Eduardo Rolim
quelle
0

Im einfachen Fall (ohne verteilte Ressourcen) können Sie die Transaktion als Ressource betrachten, bei der der Vorgang des Erstellens das Endziel erreicht.

Um zwischen <url-base>/account/aund zu übertragen <url-base>/account/b, können Sie Folgendes an posten <url-base>/transfer.

<transfer>
    <von> <url-base> / account / a </ from>
    <to> <url-base> / account / b </ to>
    <Betrag> 50 </ Betrag>
</ transfer>

Dies würde eine neue Übertragungsressource erstellen und beispielsweise die neue URL der Übertragung zurückgeben <url-base>/transfer/256.

Im Moment der erfolgreichen Veröffentlichung wird dann die "echte" Transaktion auf dem Server ausgeführt und der Betrag von einem Konto entfernt und einem anderen hinzugefügt.

Dies gilt jedoch nicht für eine verteilte Transaktion (wenn beispielsweise 'a' bei einer Bank hinter einem Dienst und 'b' bei einer anderen Bank hinter einem anderen Dienst gehalten wird) - außer zu sagen: "Versuchen Sie, alle zu formulieren." Operationen auf eine Weise, die keine verteilten Transaktionen erfordert ".

Phasmal
quelle
2
Wenn Sie nicht "alle Vorgänge so formulieren können, dass keine verteilten Transaktionen erforderlich sind", benötigen Sie wirklich ein Zwei-Phasen-Commit. Die beste Idee, die ich für die Implementierung eines zweiphasigen Commits auf REST finden konnte, ist rest.blueoxen.net/cgi-bin/wiki.pl?TwoPhaseCommit , wodurch der URL-Namespace nicht durcheinander gebracht wird und ein zweiphasiges Commit überlagert werden kann saubere REST-Semantik.
Phasmal
3
Das andere Problem bei diesem Vorschlag ist, dass Sie zwei Übertragungen erhalten, wenn ein Cache zweimal Schluckauf und POSTs verursacht.
Jon Watte
Richtig, in diesem Fall benötigen Sie einen zweistufigen Prozess - erstellen Sie eine "Übertragungs" -Ressource mit einer eindeutigen URL und fügen Sie die Übertragungsdetails als Teil des Commits hinzu (zwei Teile, wie in den anderen Antworten erwähnt). Dies könnte dann natürlich so formuliert werden, dass eine "Transaktions" -Ressource erstellt und ihr dann eine "Übertragungs" -Operation hinzugefügt wird.
Phasmal
-3

Ich denke, Sie könnten die TAN in die URL / Ressource aufnehmen:

  1. PUT / Transaktion, um die ID zu erhalten (zB "1")
  2. [PUT, GET, POST, was auch immer] / 1 / account / bob
  3. [PUT, GET, POST, was auch immer] / 1 / account / bill
  4. LÖSCHEN / Transaktion mit ID 1

Nur eine Idee.

Bis
quelle
Ich sehe zwei Probleme bei diesem Ansatz: 1) Dies bedeutet, dass Sie außerhalb einer Transaktion nicht auf eine Ressource zugreifen können (obwohl dies möglicherweise keine große Sache ist). 2) Keine der bisherigen Antworten hat die Tatsache berührt, dass der Server nicht mehr zustandslos ist, obwohl ich vermute, dass nichts dagegen unternommen werden kann.
Gili
Nun, / 1 / account / bob und / account / bob sind nur zwei verschiedene Ressourcen. :) Und RE: zustandslos bedeutet dies, dass die Ressource immer verfügbar ist und nicht von einer vorherigen Anforderung abhängt. Da Sie nach Transaktionen gefragt haben, ist dies nicht der Fall. Aber andererseits wollten Sie Transaktionen.
Bis zum
1
Wenn ein Client URIs zusammenstellen muss, ist Ihre API nicht RESTful.
Aehlke
1
Ich kann euch wirklich nicht verstehen! Wenn Sie eine Transaktion als Ressource behandeln (wie im obigen Beispiel), beenden Sie einfach die Behandlung von Transaktionen im klassischen Sinne und verwenden sie auf "die richtige REST-Weise", was die Programmierung von Transaktionsprozessen weiter vereinfacht. Sie können beispielsweise eine href in die Transaktion in Ihre Antworten aufnehmen, um die Verschiebung in einer verteilten serverseitigen Umgebung zu umgehen. Sie ist immer noch zustandslos (es ist nur eine Ressource, nicht wahr?) Und Sie können den eigentlichen Transaktionsmechanismus trotzdem implementieren wollen (was ist, wenn Sie keine DB im Rücken haben?)
Matthias Hryniszak
1
Auf die eine oder andere Weise wird alles einfach, wenn Sie einfach aufhören, an SQL / SOAP zu denken, und anfangen, an HTTP zu denken (wie es der Browser tut)
Matthias Hryniszak