it-swarm.com.de

Transaktionen in REST?

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 US-Dollar von Bobs Bankkonto auf Johns Konto.

Soweit ich das beurteilen kann, ist die einzige Möglichkeit, dies umzusetzen, das Betrügen. Sie können POST an die Ressource senden, die entweder John oder Bob zugeordnet ist, und den gesamten Vorgang mit einer einzigen Transaktion ausführen. Für mich ist dies ein Verstoß gegen die REST - Architektur, da Sie im Wesentlichen einen RPC-Aufruf durch POST tunneln, anstatt wirklich mit einzelnen Ressourcen zu arbeiten.

139
Gili

Stellen Sie sich ein RESTful-Einkaufskorb-Szenario vor. Der Warenkorb ist konzeptionell Ihr Transaktions-Wrapper. Auf die gleiche Weise, wie Sie einem Einkaufskorb mehrere Artikel hinzufügen und diesen dann zur Bearbeitung der Bestellung übermitteln 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 Bestandteilen POSTEN/PUTEN.

85
Darrel Miller

Es gibt ein paar wichtige Fälle, die von dieser Frage nicht beantwortet werden, was ich schade finde, weil sie bei Google einen hohen Stellenwert für die Suchbegriffe hat :-)

Im Einzelnen wäre eine Nice-Eigenschaft: Wenn Sie POST zweimal (weil ein Cache in der Zwischenzeit hiccupped)), sollten Sie den Betrag nicht zweimal übertragen.

Dazu legen Sie eine Transaktion als Objekt an. Dies könnte alle Ihnen bereits bekannten Daten enthalten 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 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 Zustand zurückgeben. Insbesondere würde der zweite PUT erkennen, dass sich der erste bereits im entsprechenden Status befindet, und ihn einfach zurückgeben - oder, 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 einwandfrei. Sie können zusätzlich Timeouts für Transaktionen einführen, die Sie sogar mit Expires-Headern ausdrücken können, wenn Sie möchten.

53
Jon Watte

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

POST /transaction

Dies gibt eine Transaktions-ID zurück, z. "1234" und entsprechende URL "/ transaction/1234". Beachten Sie, dass das mehrfache Auslösen von POST) nicht die gleiche Transaktion mit mehreren IDs erzeugt und auch die Einführung eines "ausstehenden" Status vermeidet. Außerdem kann POST= ' t immer idempotent sein (eine REST) Anforderung), so ist es im Allgemeinen empfehlenswert, Daten in POSTs zu minimieren.

Sie können die Erstellung einer Transaktions-ID dem Kunden ü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 er bereits vorhanden ist. In der Fehlerantwort könnte der Server einen aktuell nicht verwendeten zurückgeben ID mit einer geeigneten URL 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 (PUT HTTP-Methode) wir die Transaktion mit allen Daten 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 die eindeutige Kontonummer von John sein.

30
Tuckster

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

Aber er spricht von "Repräsentationszustandsübertragung" als Zustandsmaschine, mit Verbindungen, die zum nächsten Zustand übergehen. Auf diese Weise verfolgen die Dokumente (die Darstellungen) den Client-Status, 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 scheint mir vernünftig, dass der Server beim Hochladen automatisch zugehörige Ressourcen erstellt und Ihnen die Links zu diesen bereitstellt, damit der Server etwas für Sie verarbeitet (tatsächlich ist dies nicht der Fall) Sie müssen sie nicht automatisch erstellen: Sie können nur die Verknüpfungen erkennen und erst erstellen, wenn und wenn Sie ihnen folgen (verzögertes Erstellen). Damit Sie auch Links zum Erstellen neuer zugehöriger Ressourcen erhalten, hat eine zugehörige Ressource denselben URI, ist jedoch länger (fügt ein Suffix hinzu). Beispielsweise:

  1. Sie laden ( POST ) die Darstellung des Konzepts eines Transaktion mit allen Informationen. Dies sieht genauso aus wie ein RPC-Aufruf, aber es schafft wirklich die "vorgeschlagene Transaktionsressource". z. B. URI: /transaction Durch Pannen werden mehrere solcher Ressourcen mit jeweils unterschiedlichen URIs erstellt.
  2. In der Antwort des Servers wird der URI der erstellten Ressource und deren Darstellung angegeben. Dazu gehört der Link ( URI ), um die zugehörige Ressource von eine neue "festgeschriebene" Ressource) zu erstellen 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. Dies ist logischerweise ein Teil der Ressource, die auf dem Server erstellt wurde, und geht über die vom Client bereitgestellten Informationen hinaus. z. B. URIs: /transaction/1234/proposed, /transaction/1234/committed
  3. Sie POSTEN auf den Link zu erstellen Sie die "festgeschriebene Transaktionsressource", die diese Ressource erstellt und den Status des Servers ändert ( die Salden der beiden Konten) **. Diese Ressource kann naturgemäß nur einmal erstellt und nicht aktualisiert werden. Daher können keine Störungen auftreten, die viele Transaktionen ausführen.
  4. Sie können diese beiden Ressourcen abrufen, um ihren Status zu ermitteln. Unter der Annahme, dass ein POST andere Ressourcen ändern kann, wird der Vorschlag jetzt als "festgeschrieben" (oder möglicherweise gar nicht verfügbar) gekennzeichnet.

Dies ist vergleichbar mit der Funktionsweise von Webseiten. Die letzte Webseite lautet "Sind Sie sicher, dass Sie dies tun möchten?". Diese letzte Webseite ist selbst eine Darstellung des Status der Transaktion, die einen Link zum nächsten Status enthält. Nicht nur finanzielle Transaktionen; auch (zB) Vorschau dann auf Wikipedia festschreiben. Ich vermute, der Unterschied in REST ist, dass jede Stufe in der Folge von Zuständen einen expliziten Namen hat (seine URI).

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

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

Die wichtige Frage ist jedoch: Was sind die Vorteile dieses Ansatzes? 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 über das Speichern/Abrufen/Aktualisieren/Löschen hinaus hilfreich? Ich denke, der Hauptvorteil von REST ist die Skalierbarkeit; ein Aspekt davon ist, dass der Client-Status nicht explizit beibehalten werden muss (sondern dass er in der URI der Ressource und den nächsten Status als Links impliziert wird in seiner Darstellung) .In diesem Sinne hilft es. Vielleicht hilft dies auch beim Layering/Pipelining. OTOH wird nur der eine Benutzer seine spezifische Transaktion betrachten, so dass es keinen Vorteil hat, sie zwischenzuspeichern, damit andere sie lesen können, der große Gewinn für http .

19
13ren

Wenn Sie zurücktreten, 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 es bei nicht trivialen Transaktionen der Fall ist Englisch: emagazine.credit-suisse.com/app/art ... = 157 & lang = en Warum sollten Sie alle vorgeschlagenen Schritte für Client und Server durchgehen, um pedantisch einem Prinzip zu folgen, das nicht zum Problem passt? Anwendung.

Zusammenfassend gesagt, sollten Sie keine RESTful-API erstellen, wenn Sie wirklich viele Transaktionen (Typen, keine Instanzen) in Ihrer Anwendung ausführen.

11
Peris

Sie müssten Ihre eigene "Transaktions-ID" für das TX-Management rollen. 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 das Speichern der Aktionen in einer Datenbank (wenn Lastausgleich) oder im Arbeitsspeicher oder ähnlichem erledigen und dann Commit, Rollback, Timeout erledigen.

Nicht wirklich ein erholsamer Tag im Park.

9
TheSoftwareJedi

Ich bin seit 10 Jahren von diesem Thema abgewichen. Wenn ich zurückkomme, kann ich nicht glauben, dass sich die Religion als Wissenschaft tarnt, in die Sie sich einarbeiten, wenn Sie Google Rest + Reliable verwenden. Die Verwirrung ist mythisch.

Ich würde diese breite Frage in drei Teile aufteilen:

  • Downstream-Dienste. Jeder Webdienst, den Sie entwickeln, verfügt über Downstream-Dienste, die Sie verwenden und deren Transaktionssyntax Sie nur befolgen müssen. Sie sollten versuchen, all dies vor Benutzern Ihres Dienstes zu verbergen und sicherstellen, dass alle Teile Ihres Vorgangs als Gruppe erfolgreich sind oder fehlschlagen, und dieses Ergebnis dann an Ihre Benutzer zurückgeben.
  • Ihre Dienste. Kunden möchten eindeutige Ergebnisse für Web-Service-Aufrufe, und das übliche REST Muster, POST-, PUT- oder DELETE-Anforderungen direkt auf inhaltlichen Ressourcen zu stellen, scheint mir ein schlechter und leicht zu verbessernder Weg zu sein, um diese Gewissheit 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. Verwenden Sie für vom Server generierte IDs eine Preflight-Anforderungsantwort, um die ID der Aktion auszutauschen. Wenn diese Anforderung fehlschlägt oder zur Hälfte erfolgreich ist, wiederholt der Client die Anforderung einfach. Nicht verwendete IDs schaden nicht.

    Dies ist wichtig, da dadurch alle nachfolgenden Anforderungen in dem Sinne vollständig idempotent sind, dass sie bei n-maliger Wiederholung dasselbe Ergebnis zurückgeben und nichts weiter bewirken. Der Server speichert alle Antworten für die Aktions-ID. Wenn dieselbe Anforderung angezeigt wird, wird dieselbe Antwort wiederholt. Eine ausführlichere Beschreibung des Musters finden Sie in this google doc . Das Dokument schlägt eine Implementierung vor, die meines Erachtens (!) Im Großen und Ganzen 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 nachgelagerte Transaktionen vorliegen oder nicht.
  • Integration Ihres Dienstes in "Transaktionen", die von vorgelagerten Diensten gesteuert werden. Im Zusammenhang mit Webdiensten werden vollständige ACID-Transaktionen als normalerweise nicht lohnenswert angesehen. Sie können jedoch den Verbrauchern Ihres Dienstes erheblich helfen, indem Sie in Ihrer Bestätigungsantwort Verknüpfungen zum Abbrechen und/oder Bestätigen angeben und auf diese Weise Transaktionen) erzielen durch Entschädigung .

Ihre Anforderung ist von grundlegender Bedeutung. Lassen Sie sich nicht sagen, dass Ihre Lösung nicht koscher ist. Beurteilen Sie ihre Architekturen im Hinblick darauf, wie gut und wie einfach sie Ihr Problem angehen.

9
bbsimonbb

Zuallererst ist das Überweisen von Geld nichts, was Sie mit einem einzigen Ressourcenaufruf nicht tun können. Die Aktion, die Sie ausführen möchten, ist das Senden von Geld. So fügen Sie 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, auch bekannt als. Geld von A nach B senden.


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

Wenn Sie etwas sehr Komplexes mit vielen Ressourcen in einem definierten Kontext mit vielen Einschränkungen tun möchten, die tatsächlich die Grenze zwischen Was und Warum überschreiten (Geschäfts- und Implementierungswissen), 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 von der 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 anderes verwendet werden.

Auf diese Weise kann ein interner Status weitergegeben werden und die Verwendung von Verschlüsselung und Signierung kann weiterhin sicher und zuverlässig sein. Die richtige Abstraktion für den Kunden zu finden, warum er Zustandsinformationen weitergibt, hängt vom Design und der Architektur ab.


Die echte Lösung:

Denken Sie daran, dass REST spricht HTTP und HTTP kommt mit dem Konzept der Verwendung von Cookies. Diese Cookies werden oft vergessen, wenn Leute über REST API und Workflows und Interaktionen sprechen, die mehrere umfassen Ressourcen oder Anfragen.

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 statusbezogene Informationen zu erinnern (z. B. Artikel in einem Einkaufswagen) oder um die Browsing-Aktivitäten des Benutzers aufzuzeichnen (z. B. 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 einen Cookie. Es wurde aus genau demselben Grund entwickelt, es ist HTTP und daher kompatibel mit REST by design :).


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 an Clients macht alles komplexer. Um dies zu vermeiden, überlegen Sie einfach, was wir tun, wenn wir komplexe Interaktionen und Dinge in der realen Welt tun müssen. Wir setzen einen Agenten ein.

Mit der Agent - Metapher können Sie eine Ressource bereitstellen, die alle für Sie erforderlichen Schritte ausführen kann, und die tatsächliche Zuweisung/Anweisung, auf die sie sich bezieht, in ihrer Liste speichern (wir können also POST für den Agenten oder eine Agentur').

Ein komplexes Beispiel:

Haus kaufen:

Sie müssen Ihre Glaubwürdigkeit unter Beweis stellen (wie das Bereitstellen Ihrer Polizeiaufzeichnungen), Sie müssen finanzielle Details sicherstellen, Sie müssen das eigentliche Haus mithilfe eines Anwalts und eines vertrauenswürdigen Dritten kaufen, der die Gelder aufbewahrt, und Sie müssen überprüfen, ob das Haus jetzt Ihnen gehört und Fügen Sie das Kaufmaterial zu Ihren Steuerunterlagen usw. hinzu (nur als Beispiel, einige Schritte können falsch sein oder was auch immer).

Es kann mehrere Tage dauern, bis diese Schritte abgeschlossen sind. Einige können parallel usw. ausgeführt werden.

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

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

Getan. Die Agentur sendet Ihnen einen Verweis an Sie zurück, mit dem Sie den Status dieses Auftrags anzeigen und nachverfolgen können. Der Rest wird automatisch von den Agenten der Agentur erledigt.

Denken Sie beispielsweise an einen Bug-Tracker. Grundsätzlich melden Sie den Fehler und können die Fehler-ID verwenden, um zu überprüfen, was los ist. Sie können sogar einen Dienst verwenden, um Änderungen dieser Ressource zu überwachen. Mission erledigt.

3
Martin Kersten

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 tatsächlich etwas in REST das besagt, dass Sie abhängige Objekte in Geschäftsfällen, die dies erfordern, nicht berühren können.

Ich denke wirklich, dass es sich nicht lohnt, einen zusätzlichen Rahmen für die Erstellung eines benutzerdefinierten Transaktionsmanagers zu erstellen, wenn Sie dazu einfach die Datenbank nutzen können.

2
Toby Hede

In REST dürfen keine serverseitigen Transaktionen verwendet werden.

Eine der REST Bedingungen:

Staatenlos

Die Client-Server-Kommunikation wird weiterhin dadurch eingeschränkt, dass zwischen den Anforderungen kein Client-Kontext auf dem Server gespeichert wird. Jede Anforderung von einem Client enthält alle Informationen, die zur Bearbeitung der Anforderung erforderlich sind, und jeder Sitzungsstatus wird im Client gespeichert.

Die einzige Möglichkeit für REST besteht darin, ein Transaktionswiederholungsprotokoll zu erstellen und es in den Clientstatus zu versetzen. Bei den Anfragen sendet der Client das Redo-Log und der Server wiederholt die Transaktion und

  1. setzt 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 serversitzungsbasierte Technologie zu verwenden, die serverseitige Transaktionen unterstützt.

1
bebbo

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 Verbindungsabbruch keine von der API gespeicherte Duplizität impliziert.

Ich denke, die Verwendung eines vom Kunden generierten GUID Feldes zusammen mit dem Überweisungsobjekt und das Sicherstellen, dass dieselbe GUID nicht erneut eingefügt wurde, wäre eine einfachere Lösung für die Banküberweisung Angelegenheit.

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

Ich habe einen Artikel zu diesem Thema gefunden, in dem die Erfahrungen von mgang mit der Transaktionsatomarität in RESTful-Diensten .

1
Eduardo Rolim

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

Um also zwischen <url-base>/account/a Und <url-base>/account/b Zu wechseln, können Sie Folgendes in <url-base>/transfer Posten.

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

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

Im Moment des erfolgreichen Postings 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 verteilte Transaktionen (wenn beispielsweise "a" bei einer Bank hinter einem Service und "b" bei einer anderen Bank hinter einem anderen Service gehalten wird) - außer "versuchen, alle auszudrücken" Operationen auf eine Weise, die keine verteilten Transaktionen erfordert ".

0
Phasmal