it-swarm.com.de

REST Complex / Composite / Nested Resources

Ich versuche, die beste Methode zu finden, um Konzepte in einer REST= basierten API anzusprechen. Flache Ressourcen, die keine anderen Ressourcen enthalten, sind kein Problem. Wo ich auf Probleme stoße sind die komplexen Ressourcen.

Zum Beispiel habe ich eine Ressource für ein Comicbuch. ComicBook hat alle möglichen Eigenschaften wie author, issue number, date usw.

Ein Comic hat auch eine Liste von 1..n deckt. Diese Abdeckungen sind komplexe Objekte. Sie enthalten viele Informationen über das Cover: den Künstler, ein Datum und sogar ein mit 64-Bit-Code versehenes Bild des Covers.

Für ein GET auf ComicBook könnte ich einfach den Comic und alle Cover einschließlich ihrer base64'ed-Bilder zurückgeben. Das ist wahrscheinlich keine große Sache, um einen einzigen Comic zu bekommen. Angenommen, ich erstelle eine Client-App, in der alle Comics im System in einer Tabelle aufgelistet werden sollen.
Die Tabelle enthält einige Eigenschaften aus der Ressource ComicBook, aber wir werden sicherlich nicht alle Cover in der Tabelle anzeigen wollen. Das Zurücksenden von 1000 Comic-Büchern mit jeweils mehreren Deckblättern würde dazu führen, dass unglaublich viele Daten über das Kabel fließen - Daten, die für den Endbenutzer in diesem Fall nicht erforderlich sind.

Mein Instinkt ist es, Cover zu einer Ressource zu machen und ComicBook Cover zu enthalten. Nun ist Cover eine URI. GET für Comic-Bücher funktioniert jetzt, anstatt der riesigen Cover -Ressource senden wir eine URI für jedes Cover zurück, und Kunden können die Cover-Ressourcen nach Bedarf abrufen.

Jetzt habe ich ein Problem mit dem Erstellen neuer Comics. Mit Sicherheit möchte ich mindestens ein Cover erstellen, wenn ich ein Comic erstelle. Das ist wahrscheinlich eine Geschäftsregel.
Jetzt stecke ich fest. Entweder zwinge ich die Kunden zur Durchsetzung von Geschäftsregeln, indem ich zuerst eine Cover sende, die URI für diese Deckung erhalte und dann POST eine ComicBook sende. mit diesem URI in der Liste oder mit meinem POST in ComicBook wird eine anders aussehende Ressource verwendet als ausgespuckt. Die eingehenden Ressourcen für POST und GET sind tiefe Kopien, wobei die ausgehenden GET Verweise auf abhängige Ressourcen enthalten.

Die Ressource Cover ist wahrscheinlich in jedem Fall erforderlich, da ich sicher bin, dass ich als Kunde in einigen Fällen die Deckungsrichtung ansprechen möchte. Das Problem liegt also in allgemeiner Form vor, unabhängig von der Größe der abhängigen Ressource. Wie gehen Sie im Allgemeinen mit komplexen Ressourcen um, ohne dass der Client nur wissen muss, wie diese Ressourcen zusammengesetzt sind?

168
jgerman

@ray, ausgezeichnete Diskussion

@jgerman, vergiss das nicht, nur weil es REST ist, heißt das nicht, dass die Ressourcen von POST in Stein gemeißelt werden müssen.

Was Sie in eine bestimmte Darstellung einer Ressource aufnehmen möchten, bleibt Ihnen überlassen.

Bei den Deckblättern, auf die separat verwiesen wird, handelt es sich lediglich um die Erstellung einer übergeordneten Ressource (Comic), auf deren untergeordnete Ressourcen (Deckblätter) verwiesen werden kann. Beispielsweise möchten Sie möglicherweise auch Verweise auf Autoren, Verleger, Charaktere oder Kategorien separat bereitstellen. Möglicherweise möchten Sie diese Ressourcen separat oder vor dem Comic erstellen, in dem sie als untergeordnete Ressourcen aufgeführt sind. Alternativ können Sie beim Erstellen der übergeordneten Ressource neue untergeordnete Ressourcen erstellen.

Ihr spezieller Fall der Umschläge ist insofern etwas komplexer, als ein Umschlag wirklich ein Comicbuch erfordert und umgekehrt.

Wenn Sie jedoch eine E-Mail-Nachricht als Ressource und die Absenderadresse als untergeordnete Ressource betrachten, können Sie natürlich immer noch separat auf die Absenderadresse verweisen. Holen Sie sich zum Beispiel alles von Adressen. Oder erstellen Sie eine neue Nachricht mit einer früheren Absenderadresse. Wenn es sich bei E-Mail um REST handelte, konnten Sie leicht erkennen, dass viele Ressourcen mit Querverweisen verfügbar waren:/empfangene Nachrichten,/Entwurfsnachrichten,/Absenderadressen,/Anschriften,/Adressen,/Betreffe,/Anhänge,/Ordner ,/tags,/categories,/labels et al.

Dieses Tutorial bietet ein großartiges Beispiel für Ressourcen, auf die verwiesen wird. http://www.peej.co.uk/articles/restfully-delicious.html

Dies ist das häufigste Muster für automatisch generierte Daten. Beispielsweise veröffentlichen Sie keinen URI, keine ID oder kein Erstellungsdatum für die neue Ressource, da diese vom Server generiert werden. Sie können jedoch den URI, die ID oder das Erstellungsdatum abrufen, wenn Sie die neue Ressource zurückerhalten.

Ein Beispiel für Ihren Fall von Binärdaten. Beispielsweise möchten Sie Binärdaten als untergeordnete Ressourcen bereitstellen. Wenn Sie die übergeordnete Ressource erhalten, können Sie diese untergeordneten Ressourcen als dieselben Binärdaten oder als URIs darstellen, die die Binärdaten darstellen.

Formulare und Parameter unterscheiden sich bereits von den HTML-Darstellungen der Ressourcen. Das Posten eines Binär-/Dateiparameters, der zu einer URL führt, ist keine Strecke.

Wenn Sie das Formular für eine neue Ressource (/ comic-books/new) oder das Formular zum Bearbeiten einer Ressource (/ comic-books/0/edit) erhalten, fragen Sie nach einer formulartypischen Darstellung der Ressource. Wenn Sie es mit dem Inhaltstyp "application/x-www-form-urlencoded" oder "multipart/form-data" in die Ressourcensammlung hochladen, bitten Sie den Server, diese Typdarstellung zu speichern. Der Server kann mit der gespeicherten HTML-Darstellung oder was auch immer antworten.

Möglicherweise möchten Sie auch zulassen, dass eine HTML-, XML- oder JSON-Darstellung für API-Zwecke oder ähnliche Zwecke in der Ressourcensammlung veröffentlicht wird.

Es ist auch möglich, Ihre Ressourcen und Ihren Workflow so darzustellen, wie Sie es beschreiben. Dabei werden Titelbilder berücksichtigt, die nach dem Comic-Buch veröffentlicht wurden, für die jedoch ein Deckblatt erforderlich ist. Beispiel wie folgt.

  • Ermöglicht die verzögerte Erstellung von Deckblättern
  • Ermöglicht die Erstellung von Comics mit dem erforderlichen Deckblatt
  • Ermöglicht das Querverweisen von Deckblättern
  • Ermöglicht mehrere Abdeckungen
  • Erstelle ein Comic-Heft
  • Erstellen Sie Comic-Coverentwürfe
  • Veröffentlichen Sie den Comic-Entwurf

GET/Comic-Bücher
=> 200 OK, hol alle Comics.

GET/Comics/0
=> 200 OK, Comic (ID: 0) mit Covers (/ Covers/1,/Covers/2) herunterladen.

GET/Comic-Bücher/0/Covers
=> 200 OK, Cover für Comicbuch abrufen (ID: 0).

GET/deckt
=> 200 OK, alle Deckblätter holen.

GET/covers/1
=> 200 OK, gehe in Deckung (id: 1) mit dem Comic (/ comic-books/0).

GET/Comic-Bücher/neu
=> 200 OK, Formular zum Erstellen eines Comics anfordern (Formular: POST/draft-comic-books)).

POST/Draft-Comic-Bücher
title = foo
author = boo
publisher = goo
veröffentlicht = 01.01.2011
=> 302 gefunden, Ort:/draft-comic-books/3, Weiterleitung zum Entwurf eines Comic-Buches (ID: 3) mit Umschlägen (binär).

GET/comic-entwürfe/3
=> 200 OK, Entwurf eines Comic-Buches (ID: 3) mit Umschlägen abrufen.

GET/entwurf-comic-bücher/3/cover
=> 200 OK, Cover für Comic-Entwurf (/ draft-comic-book/3) abrufen.

GET/draft-comic-books/3/cover/neu
=> 200 OK, Formular zum Erstellen eines Deckblatts für ein Comic-Heft (/ draft-comic-book/3) abrufen (Formular: POST/draft-comic-books/3)/umfasst).

POST/Draft-Comic-Bücher/3/Covers
cover_type = front
cover_data = (binär)
=> 302 gefunden, Fundort:/draft-comic-books/3/Cover, Weiterleitung zum neuen Cover für den Entwurf eines Comics (/ draft-comic-book/3/Cover/1).

GET/draft-comic-books/3/veröffentlichen
=> 200 OK, Formular zum Veröffentlichen des Comic-Entwurfs (ID: 3) abrufen (Formular: POST/published-comic-books)).

POST/Veröffentlichte Comics
title = foo
author = boo
publisher = goo
veröffentlicht = 01.01.2011
cover_type = front
cover_data = (binär)
=> 302 gefunden, Fundort:/comic-books/3, Weiterleitung zum veröffentlichten Comic (id: 3) mit Umschlägen.

62
Alex

Das Behandeln von Deckungen als Ressourcen ist definitiv im Sinne von REST, insbesondere von HATEOAS. Also ja, eine GET Anfrage an http://example.com/comic-books/1 gibt Ihnen eine Darstellung von Buch 1 mit Eigenschaften, einschließlich einer Reihe von URIs für Cover. So weit, ist es gut.

Ihre Frage ist, wie Sie mit der Erstellung von Comics umgehen sollen. Wenn Ihre Geschäftsregel war, dass ein Buch oder mehr Umschläge haben würde, dann haben Sie kein Problem:

POST http://example.com/comic-books

mit coverless comic book data erstellt ein neues comic book und gibt die vom server generierte id zurück (sagen wir es kommt als 8 zurück), und jetzt können sie covers wie folgt hinzufügen:

POST http://example.com/comic-books/8/covers

mit der Abdeckung in der Einheit Körper.

Jetzt haben Sie eine gute Frage, was passiert, wenn Ihre Geschäftsregel besagt, dass es immer mindestens eine Deckung geben muss. Hier sind einige Auswahlmöglichkeiten, von denen Sie die erste in Ihrer Frage identifiziert haben:

  1. Erzwingen Sie zuerst die Erstellung eines Covers und machen Sie das Cover nun im Wesentlichen zu einer nicht abhängigen Ressource, oder platzieren Sie das ursprüngliche Cover im Entity-Body des POST), mit dem das Comic-Buch erstellt wird bedeutet, dass die Darstellung, die Sie POST erstellen, von der Darstellung abweicht, die Sie erhalten haben.

  2. Definieren Sie den Begriff einer primären oder initialen oder bevorzugten oder anderweitig bezeichneten Deckung. Dies ist wahrscheinlich ein Modellierungs-Hack, und wenn Sie dies getan haben, wäre es, als würden Sie Ihr Objektmodell (Ihr konzeptionelles oder Geschäftsmodell) optimieren, um es an eine Technologie anzupassen. Keine gute Idee.

Sie sollten diese beiden Möglichkeiten gegen das einfache Zulassen von Comics ohne Cover abwägen.

Welche der drei Möglichkeiten solltest du wählen? Ich weiß nicht zu viel über Ihre Situation, beantworte aber die allgemeine 1..N-abhängige Ressourcenfrage. Ich würde sagen:

  • Wenn Sie für Ihre RESTful-Service-Schicht auf 0..N setzen können, ist das großartig. Vielleicht kann eine Schicht zwischen Ihrem RESTful SOA) die weitere geschäftliche Einschränkung bewältigen, wenn mindestens eine erforderlich ist. (Ich bin mir nicht sicher, wie das aussehen würde, aber es könnte sich lohnen, sie zu erkunden. Normalerweise sieht man die SOA sowieso.)

  • Wenn Sie lediglich eine 1..N-Einschränkung modellieren müssen, fragen Sie sich, ob Covers möglicherweise nur gemeinsam nutzbare Ressourcen sind, mit anderen Worten, sie existieren möglicherweise nicht nur für Comic-Bücher. Jetzt sind sie keine abhängigen Ressourcen, und Sie können sie zuerst erstellen und URIs in Ihrem POST, das Comic-Bücher erstellt) angeben.

  • Wenn Sie 1..N benötigen und die Deckung abhängig bleibt, entspannen Sie einfach Ihren Instinkt, um die Darstellungen in POST und GET gleich zu halten oder sie gleich zu machen.

Der letzte Punkt wird folgendermaßen erklärt:

<comic-book>
  <name>...</name>
  <edition>...</edition>
  <cover-image>...BASE64...</cover-image>
  <cover-image>...BASE64...</cover-image>
  <cover>...URI...</cover>
  <cover>...URI...</cover>
</comic-book>

Wenn Sie POST vorhandene Uris zulassen, wenn Sie sie haben (aus anderen Büchern entlehnt), aber auch ein oder mehrere Ausgangsbilder einfügen. Bild, geben Sie eine 409 oder eine ähnliche Antwort zurück. Auf GET können Sie URIs zurückgeben.

Im Grunde erlauben Sie also, dass die Darstellungen POST und GET "gleich" sind, aber Sie wählen nur, dass Sie weder das Titelbild für GET noch das Titelbild für POST "verwenden". Hoffe, dass das Sinn macht.

43
Ray Toal