it-swarm.com.de

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

Ich bin gerade dabei, eine REST API) zu erstellen und stoße derzeit auf das folgende Problem:

  • Foo ist die erste Ressource. CRUD-Operationen können über den URI /foo/ Angewendet werden.
  • Bar ist die zweite Ressource. CRUD-Operationen können über den URI /bar/ Angewendet werden.
  • Jedes Foo ist null oder eins Bar zugeordnet. Der Grund, warum ich Bar nicht als Unterressource von Foo behandle, ist, dass dieselbe Bar -Instanz von mehreren Foos gemeinsam genutzt werden kann. Also dachte ich, es ist besser, über eine unabhängige URI darauf zuzugreifen, anstatt über /foo/[id]/bar.

Mein Problem ist, dass in einer erheblichen Anzahl von Fällen Clients, die nach einer Foo -Instanz fragen, auch an der zugehörigen Bar -Instanz 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 müsste (z. B. ein Container wie { foo: ..., bar: ... } Anstelle nur eines serialisierten Foo) macht den Ressourcenendpunkt Foo "heterogen". Ich denke nicht, dass das eine gute Sache ist. Bei der Abfrage von /foo Sollten Clients unabhängig von den Abfrageparametern immer dieselbe Ressourcendarstellung (Struktur) erhalten.
  • Eine andere Idee besteht darin, einen neuen schreibgeschützten Endpunkt einzuführen, z. /fooandbar/[foo-id]. In diesem Fall ist es kein Problem, eine Darstellung wie { foo: ..., bar: ... } Zurückzugeben, da es sich dann nur um die "offizielle" Darstellung der Ressource fooandbar handelt. Ich weiß jedoch nicht, ob ein solcher Helfer-Endpunkt wirklich RESTful ist (aus diesem Grund 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 denken Sie? Gibt es noch andere Möglichkeiten?

9
ceran

Eine Stufe REST API würde Ihnen ein Foo und auch einen Link zurückgeben, der das verwandte Bar angibt.

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, produziert ein Level 3 REST) API 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..
        }
      ]
    }
  ]
}
5
Qwerky

Wenn 95% aller Abfragen Foo sowie Bar wünschen, geben Sie es einfach innerhalb zurück.) des Foo Objekts wenn Sie ein Foo anfordern. Fügen Sie einfach eine Eigenschaft bar (oder einen anderen Begriff für die Beziehung) hinzu und platzieren Sie das Objekt Bar dort. Wenn die Beziehung nicht existiert, verwenden Sie null.

Ich denke du überdenkst das :)

4
Nathan Merrill