Frage

Ich entwerfe einen erholsamen Webdienst mit ROA (Ressourcenorientierte Architektur).

Ich versuche, eine effiziente Möglichkeit zu erarbeiten, um Idempotenz für Put -Anforderungen zu gewährleisten, die neue Ressourcen erstellen, in denen der Server den Ressourcentaste bezeichnet.

Nach meinem Verständnis besteht der traditionelle Ansatz darin, eine Art von Transaktionsressource wie /create_person zu erstellen. Die Interaktion der Client-Server zum Erstellen einer neuen Person-Ressource wäre in zwei Teilen:

Schritt 1: Holen Sie sich eine eindeutige Transaktions -ID zum Erstellen der neuen Person Ressource :::

**Client request:**
POST /CREATE_PERSON

**Server response:**
200 OK
transaction-id:"as8yfasiob"

Schritt 2: Erstellen Sie die neue Person -Ressource in einer Anfrage, die garantiert eindeutig ist, indem Sie die Transaktions -ID ::: verwenden

**Client request**
PUT /CREATE_PERSON/{transaction_id}
first_name="Big bubba"

**Server response**
201 Created             // (If the request is a duplicate, it would send this
PersonKey="398u4nsdf"   // same response without creating a new resource.  It
                        // would perhaps send an error response if the was used
                        // on a transaction id non-duplicate request, but I have
                        // control over the client, so I can guarantee that this
                        // won't happen)

Das Problem, das ich mit diesem Ansatz sehe, ist, dass es erforderlich ist, zwei Anfragen an den Server zu senden, um einen einzelnen Betrieb der Erstellung einer neuen Person -Ressource zu erstellen. Dies schafft eine Leistungsprobleme, die die Wahrscheinlichkeit erhöht, dass der Benutzer darauf wartet, dass der Client seine Anfrage abschließt.

Ich habe versucht, Ideen für die Beseitigung des ersten Schritts wie vorübergehende Transaktions-IDs mit jeder Anfrage zu vermeiden, aber die meisten meiner Ideen haben andere Probleme oder beinhalten die Opferung der Staatenlosigkeit der Anwendung.

Gibt es eine Möglichkeit, dies zu tun?

Bearbeiten::::::

Die Lösung, mit der wir am Ende gegangen sind, war, dass der Kunde eine UUID erfuhr und sie zusammen mit der Anfrage senden konnte. Ein Uuid ist eine sehr große Zahl, die den Raum von 16 Bytes (2^128) einnimmt. Im Gegensatz zu dem, was jemand mit einem programmierenden Geist intuitiv denken könnte, ist es akzeptiert, dass sie zufällig eine UUID generieren und annehmen, dass es sich um einen einzigartigen Wert handelt. Dies liegt daran, dass die Anzahl der möglichen Werte so groß ist, dass die Wahrscheinlichkeit, zwei derselben Zahl zufällig zu generieren, niedrig genug sind, um praktisch unmöglich zu sein.

Eine Einschränkung ist, dass unsere Clients eine UUID vom Server anfordern lassen (GET uuid/). Dies liegt daran, dass wir der Umgebung, in der unser Kunde ausgeführt wird, nicht garantieren können. Wenn es ein Problem gab, beispielsweise bei der Aussaat des Zufallszahlengenerators auf dem Kunden, könnte es sehr gut eine Uuid -Kollision geben.

War es hilfreich?

Lösung

Sie verwenden das falsche HTTP -Verb für Ihre Erstellung. RFC 2616 Gibt die Semantik der Operationen für an POST und PUT.

Absatz 9.5:

POST Die Methode wird verwendet, um die Anfrage zu beantragen

Absatz 9.6

PUT Methodenanfragen, dass das beigefügte Unternehmen unter dem angegebenen Request-URI gespeichert wird.

Es gibt beispielsweise subtile Details dieses Verhaltens PUT Kann verwendet werden, um neue Ressourcen an der angegebenen URL zu erstellen, wenn es noch nicht vorhanden ist. Jedoch, POST sollte die neue Entität niemals auf die Anfrage -URL und die URL setzen PUT sollte immer eine neue Entität auf die Anfrage -URL setzen. Diese Beziehung zur Anfrage -URL definiert POST wie CREATE und PUT wie UPDATE.

Nach dieser semantischen Semantik, wenn Sie verwenden möchten PUT Um eine neue Person zu erstellen, sollte sie in erstellt werden in /CREATE_PERSON/{transaction_id}. Mit anderen Worten, die von Ihrer erste Anfrage zurückgegebene Transaktions -ID sollte die Personschlüssel sein, mit der dieser Datensatz später abgerufen wird. Du solltest nicht machen PUT Anfrage an eine URL, die nicht der endgültige Ort dieses Datensatzes sein wird.

Besser noch, aber Sie können dies als Atomoperation durch verwenden, indem Sie a verwenden POST zu /CREATE_PERSON. Auf diese Weise können Sie eine einzige Anfrage erstellen, den neuen Personendatensatz zu erstellen und in der Antwort die neue ID zu erhalten (die auch im HTTP verwiesen werden sollte Location auch Header).

In den restlichen Richtlinien geben in der Zwischenzeit an, dass Verben nicht Teil der Ressourcen -URL sein sollten. Daher sollte die URL, um eine neue Person zu erstellen /PERSONS (Ich bevorzuge die Pluralform :-)).

So wird Ihre Ruhe -API:

  • Alle Personen bekommen - GET /PERSONS
  • Einzelperson zu bekommen - GET /PERSONS/{id}
  • neue Person erstellen - POST /PERSONS wobei der Körper die Daten für den neuen Datensatz enthält
  • So aktualisieren Sie die vorhandene Person oder erstellen Sie eine neue Person mit bekannter ID - PUT /PERSONS/{id} mit dem Körper, der die Daten für den aktualisierten Datensatz enthält.
  • Bestehende Person löschen - DELETE /PERSONS/{id}

HINWEIS: Ich persönlich bevorzuge es aus zwei Gründen nicht, Put für das Erstellen von Datensätzen zu verwenden, es sei denn -)).

Aktualisieren: Sie haben Recht POST ist nicht idempotent und das ist gemäß der HTTP -Spezifikation. POST Wille stets Gibt eine neue Ressource zurück. In Ihrem obigen Beispiel wird diese neue Ressource der Transaktionskontext sein.

Mein Punkt ist jedoch, dass Sie das wollen PUT Um eine neue Ressource (ein Person -Datensatz) und nach der HTTP -Spezifikation zu erstellen, sollte diese neue Ressource selbst an der URL gefunden werden. Insbesondere wenn Ihr Ansatz bricht, ist die URL, die Sie mit dem verwenden PUT ist eine Darstellung des Transaktionskontexts, der durch die Post erstellt wurde, keine Darstellung der neuen Ressource selbst. Mit anderen Worten, der Persondatensatz ist ein Nebeneffekt bei der Aktualisierung des Transaktionsdatensatzes, nicht das unmittelbare Ergebnis davon (der aktualisierte Transaktionsdatensatz).

Natürlich mit diesem Ansatz der PUT Die Anfrage wird idempotent sein, da nach dem Erstellen des Personalsatz PUT Anfragen werden nichts tun. Aber jetzt haben Sie ein anderes Problem - um diesen Person -Datensatz tatsächlich zu aktualisieren, müssen Sie eine machen PUT Anfrage an eine andere URL - eine, die den Personsatz darstellt, nicht die Transaktion, in der sie erstellt wurde. Jetzt haben Sie zwei separate URLs, die Ihre API -Kunden wissen und Anfragen stellen müssen, um dieselbe Ressource zu manipulieren.

Oder Sie könnten eine vollständige Darstellung des letzten Ressourcenstatus haben, der auch im Transaktionsdatensatz kopiert wurde, und die Aktualisierungen von Personenaufzeichnungen für Aktualisierungen durch die Transaktions -URL durchlaufen. Aber an diesem Punkt die Transaktions -URL ist Für die Absicht und Zweck der Person, was bedeutet, dass sie von der erstellt wurde POST Anfrage an erster Stelle.

Andere Tipps

Ich bin gerade auf diesen Beitrag gestoßen:Einfacher Beweis, dass die Richt nicht einzigartig ist

Obwohl die Frage universell verspottet ist, werden einige der Antworten in eine tiefere Erklärung von Richtlinien eingehen. Es scheint, dass eine Richtlinie eine Anzahl von 2^128 Größe beträgt und dass die Wahrscheinlichkeit, dass zwei der gleichen Anzahl dieser Größe zufällig so niedrig für alle praktischen Zwecke sind.

Vielleicht könnte der Client nur eine eigene Transaktions -ID der Größe eines GUID generieren, anstatt den Server für einen abzufragen. Wenn jemand dies diskreditieren kann, lassen Sie es mich bitte wissen.

Ich bin mir nicht sicher, ob ich eine direkte Antwort auf Ihre Frage habe, aber ich sehe einige Probleme, die zu Antworten führen können.

Ihr erster Vorgang ist ein Get, aber es ist kein sicherer Betrieb, da sie eine neue Transaktions -ID "erstellt". Ich würde vorschlagen, dass Post ein angemesseneres Verb ist.

Sie erwähnen, dass Sie besorgt über Leistungsprobleme sind, die von dem Benutzer wahrgenommen werden, der durch zwei Rundenfahrten verursacht wird. Liegt dies daran, dass Ihr Benutzer 500 Objekte gleichzeitig erstellen wird oder dass Sie mit massiven Latenzproblemen in einem Netzwerk sind?

Wenn zwei Rundreisen keine vernünftige Kosten für das Erstellen eines Objekts als Antwort auf eine Benutzeranforderung sind, würde ich vorschlagen, dass HTTP nicht das richtige Protokoll für Ihr Szenario ist. Wenn Ihr Benutzer jedoch große Mengen von Objekten gleichzeitig erstellen muss, können wir wahrscheinlich einen besseren Weg finden, um Ressourcen freizulegen, um dies zu aktivieren.

Warum verwenden Sie nicht einfach einen einfachen Beitrag, einschließlich der Nutzlast auf Ihrem ersten Anruf? Auf diese Weise sparen Sie zusätzlichen Anruf und müssen keine Transaktion hervorrufen:


POST /persons

first_name=foo

Antwort wäre:


HTTP 201 CREATED
...
payload_containing_data_and_auto_generated_id

Server-internen wird eine ID generiert. Der Einfachheit halber würde ich einen artifialen Primärschlüssel (z. B. Auto-Increment-ID aus der Datenbank) entscheiden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top