Kann eine REST-API mehrere Ressourcen als eine einzige zusammengesetzte Ressource zurückgeben?

9

Ich bin gerade dabei, eine REST-API zu erstellen, und derzeit tritt das folgende Problem auf:

  • Fooist die erste Ressource. CRUD-Operationen können über den /foo/URI angewendet werden .
  • Barist die zweite Ressource. CRUD-Operationen können über den /bar/URI angewendet werden .
  • Jeder Fooist mit Null oder Eins verbunden Bar. Der Grund, warum ich nicht Barals Unterressource von behandle, Fooist, dass dieselbe BarInstanz von mehreren geteilt werden kann Foo. Also dachte ich, es ist besser, statt über eine unabhängige URI darauf zuzugreifen /foo/[id]/bar.

Mein Problem ist, dass in einer erheblichen Anzahl von Fällen auch Clients, die nach einer FooInstanz fragen, an der zugehörigen BarInstanz interessiert sind . Derzeit bedeutet dies, dass sie zwei Abfragen anstelle von einer ausführen müssen. Ich möchte eine Methode vorstellen, mit der beide Objekte mit einer einzigen Abfrage abgerufen werden können, aber ich weiß nicht, wie ich die API dafür modellieren soll. Was ich mir bisher ausgedacht habe:

  • Ich könnte einen ähnlichen Abfrageparameter einführen : /foo/[id]?include_bar=true. Das Problem bei diesem Ansatz besteht darin, dass die Ressourcendarstellung (z. B. die JSON-Struktur) der Antwort anders aussehen muss (z. B. ein Container wie z. B. { foo: ..., bar: ... }nur ein serialisierter Foo), wodurch der FooRessourcenendpunkt "heterogen" wird. Ich denke nicht, dass das eine gute Sache ist. Bei der Abfrage /foosollten Clients unabhängig von den Abfrageparametern immer dieselbe Ressourcendarstellung (Struktur) erhalten.
  • Eine andere Idee ist die Einführung eines neuen schreibgeschützten Endpunkts, z /fooandbar/[foo-id]. In diesem Fall ist es kein Problem, eine Darstellung wie zurückzugeben { foo: ..., bar: ... }, da es sich dann nur um die "offizielle" Darstellung der fooandbarRessource handelt. Ich weiß jedoch nicht, ob ein solcher Helfer-Endpunkt wirklich RESTful ist (deshalb habe ich im Titel der Frage "can" geschrieben. Natürlich ist es technisch möglich, aber ich weiß nicht, ob es eine gute Idee ist).

Was denkst du? Gibt es noch andere Möglichkeiten?

Ceran
quelle
Wie lautet der Begriff für die Beziehung zwischen Foo und Bar? Könnten Sie sagen, dass Bar ein Elternteil von Foo ist?
Nathan Merrill
A Barkann nicht existieren, ohne mit a verbunden zu sein Foo. Wie ich oben geschrieben habe, ist es jedoch möglich, dass mehrere Foos dasselbe teilen Bar. Es sollte möglich sein, eine Fooohne Barzugehörige zu erstellen , daher denke ich nicht, Bardass als Eltern behandelt werden sollte.
Ceran
1
Ich denke , einige der Probleme auftreten ich direkt Domänenmodell hatte Beziehungen in URIs durch das Übersetzen und gleich Ressourcen zu Domain - Entitäten . Es könnte von Interesse sein, dass REST-APIs hypertextgesteuert sein müssen . Besondere Aufmerksamkeit auf den 4. Punkt
Laiv

Antworten:

5

Eine REST-API der Stufe 3 würde Ihnen einen Foound auch einen Link zurückgeben, der den entsprechenden Wert angibt Bar.

GET /foo/123
<foo id="123">
  ..foo stuff..
  <link rel="bar" uri="/bar/456"/>
</foo>

Sie können Ihrer API dann eine Drilldown-Funktion hinzufügen, die die Navigation von Links ermöglicht.

GET /foo/123?drilldown=bar
<foo id="123">
  ..foo stuff..
  <link rel="bar" uri="/bar/456">
    <bar id="456">
      ..bar stuff...
    </bar>
  </link>
</foo>

Die Drilldown-Funktion würde sich vor den APIs befinden und Antworten abfangen. Es würde die Drilldown-Anrufe tätigen und die Details eingeben, bevor die Antwort an den Anrufer zurückgegeben wird.

Dies ist eine ziemlich häufige Sache in Level 3 REST, da es die Client / Server-Chattiness über langsame http stark reduziert. Das Unternehmen, für das ich arbeite, erstellt eine REST-API der Stufe 3 mit genau dieser Funktion.

Update: Für was es wert ist, hier ist, wie es in JSON aussehen könnte. So würde unsere API es strukturieren. Beachten Sie, dass Sie Ihre Drilldowns verschachteln können, um Links von Links usw. zu ziehen.

GET /foo/123?drilldown=bar

{
  "self": {
    "type": "thing.foo",
    "uri": "/foo/123=?drilldown=bar",
    "href": "http://localhost/api/foo/123?drilldown=bar"
  },
  "links": [
    {
      "rel": "bar",
      "rev": "foo",
      "type": "thing.bar",
      "uri": "/bar/456",
      "href": "http://localhost/api/bar/456"
    }
  ],
  "_bar": [
    {
      "self": {
        "type": "thing.bar",
        "uri": "/bar/456",
        "href": "http://localhost/api/bar/456"
      },
      "links": [
        {
          ..other link..
        },
        {
          ..other link..
        }
      ]
    }
  ]
}
Qwerky
quelle
Interessanterweise verwende ich bereits Hypermedia-Links / Steuerelemente, um die enge Kopplung an URIs zu entfernen, aber ich habe nicht über die "Drilldown" -Idee nachgedacht, die sehr vielversprechend erscheint. Wie könnte eine JSON-Darstellung aussehen? Im Moment enthält jede JSON-Darstellung meiner Ressourcen ein linksArray. Jeder Eintrag ist ein Verknüpfungsobjekt mit einem relund einem uriFeld (ähnlich Ihrem XML-Beispiel). Sollte ich jedem Linkobjekt (z. B. data) nur ein drittes Feld hinzufügen ? Gibt es einen Standard?
Ceran
Der Drilldown ist eigentlich keine Ruhefunktion, daher gibt es keine Standards (zumindest die ich kenne).
Qwerky
Es gibt einige vorgeschlagene Standards, wie z. B. stateless.co/hal_specification.html, die ich in unseren Anwendungen verwende. Es ist sehr nah an Ihrem Beispiel.
Pete Kirkham
4

Wenn 95% aller Abfragen Fooso gut wie wollen Bar, geben Sie es einfach innerhalb des FooObjekts zurück, wenn Sie eine anfordern Foo. Fügen Sie einfach eine Eigenschaft bar(oder einen anderen Begriff für die Beziehung) hinzu und platzieren Sie das BarObjekt dort. Wenn die Beziehung nicht existiert, verwenden Sie null.

Ich denke du überdenkst das :)

Nathan Merrill
quelle
Ich hätte mir diese Zahl nicht einfallen lassen sollen (95%), es war ein Fehler, sorry. Was ich sagen wollte war, dass ein großer Teil der Anfragen gleichzeitig an beiden Ressourcen interessiert ist. Es gibt jedoch immer noch eine relevante Anzahl von Anfragen, die nur interessiert sind Foo, und da jede Barsehr viel Speicherplatz hat (etwa 3x-4x so groß wie Foo), möchte ich keine zurückgeben, Barwenn ein Client dies nicht explizit anfordert.
Ceran
Wie groß reden wir? Ich bezweifle , dass es geht zu machen , dass viel Unterschied in Transferzeit, und ich ziehe eine saubere API über Geschwindigkeit
Nathan Merrill