Un'API REST può restituire più risorse come una singola risorsa composta?


10

Sono in corso la creazione di un'API REST e attualmente sto riscontrando il seguente problema:

  • Fooè la prima risorsa. Le operazioni CRUD possono essere applicate tramite l' /foo/URI.
  • Barè la seconda risorsa. Le operazioni CRUD possono essere applicate tramite l' /bar/URI.
  • Ogni Fooè associato a zero o uno Bar. Il motivo per cui non considero Baruna risorsa secondaria di Fooè perché la stessa Baristanza può essere condivisa tra più utenti Foo. Quindi ho pensato che fosse meglio accedervi tramite un URI indipendente anziché /foo/[id]/bar.

Il mio problema è che in molti casi i clienti che chiedono Fooun'istanza sono interessati anche Barall'istanza associata . Attualmente, ciò significa che devono eseguire due query anziché una. Voglio introdurre un modo che consenta di ottenere entrambi gli oggetti con una singola query, ma non so come modellare l'API per farlo. Quello che mi è venuto in mente finora:

  • Potrei introdurre un parametro di query simile a questo: /foo/[id]?include_bar=true. Il problema con questo approccio è che la rappresentazione delle risorse (ad es. La struttura JSON) della risposta dovrebbe apparire diversa (ad es. Un contenitore come { foo: ..., bar: ... }invece di un semplice serializzato Foo), il che rende l' Fooendpoint della risorsa "eterogeneo". Non penso sia una buona cosa. Durante la query /foo, i client devono sempre ottenere la stessa rappresentazione (struttura) delle risorse, indipendentemente dai parametri della query.
  • Un'altra idea è quella di introdurre un nuovo endpoint di sola lettura, ad es /fooandbar/[foo-id]. In questo caso, non è un problema restituire una rappresentazione come { foo: ..., bar: ... }, perché è solo la rappresentazione "ufficiale" della fooandbarrisorsa. Tuttavia, non so se un tale endpoint di supporto sia davvero RESTful (questo è il motivo per cui ho scritto "can" nel titolo della domanda. Naturalmente è tecnicamente possibile, ma non so se sia una buona idea).

Cosa ne pensi? Ci sono altre possibilità?


Qual è il termine per la relazione tra Foo e Bar? Potresti dire che Bar è un genitore di Foo?
Nathan Merrill,

A Barnon può esistere senza essere associato a Foo. Tuttavia, come ho scritto sopra, è possibile che più Foos condividano lo stesso Bar. Dovrebbe essere possibile creare un Foosenza un Barassociato, quindi non credo che Bardovrebbe essere trattato come genitore.
ceran,

1
Penso che tu stia riscontrando alcuni dei problemi che ho avuto traducendo direttamente le relazioni del modello di dominio in URI e equiparando le risorse alle entità di dominio . Potrebbe interessare che le API REST debbano essere basate sull'ipertesto . Particolare attenzione al quarto punto
Laiv,

Risposte:


6

Un livello 3 REST API restituirebbe una Fooe anche un link con indicazione della relativa Bar.

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

È quindi possibile aggiungere una funzionalità "drill down" all'API che consente la navigazione dei collegamenti;

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

La funzionalità di drill down si posizionava davanti alle API e intercettava le risposte. Effettuerebbe le chiamate drill-down e riempire i dettagli prima di restituire la risposta al chiamante.

Questa è una cosa abbastanza comune nel REST di livello 3 in quanto riduce notevolmente la chattiness client / server su http lento. L'azienda per cui lavoro produce un'API REST di livello 3 con esattamente questa funzione.

Aggiornamento: per quello che vale, ecco come potrebbe apparire in JSON. Ecco come la nostra API la strutturerebbe. Nota che puoi annidare i drill-down per estrarre collegamenti di collegamenti ecc.

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..
        }
      ]
    }
  ]
}

Interessante, sto già usando collegamenti / controlli ipermediali per rimuovere l'accoppiamento stretto con gli URI, ma non ho pensato all'idea del "drill down" che sembra molto promettente. Come potrebbe essere una rappresentazione JSON Al momento, ogni rappresentazione JSON delle mie risorse contiene un linksarray, ogni voce è un oggetto link con un rele un uricampo (simile al tuo esempio xml). Devo solo aggiungere un terzo campo a ciascun oggetto link (ad es. data)? C'è qualche standard?
ceran,

Il drill down non è in realtà una funzione di riposo, quindi non ci sono standard (almeno che io sappia).
Qwerky,

Ci sono alcuni standard proposti, come stateless.co/hal_specification.html che sto usando nelle nostre applicazioni. È molto vicino al tuo esempio.
Pete Kirkham,

4

Se il 95% di tutte le query vogliono Foocosì come Bar, poi semplicemente restituirlo all'interno di Foooggetto quando si richiede un Foo. Aggiungi semplicemente una proprietà bar(o qualche altro termine per la relazione) e inserisci l' Baroggetto. Se la relazione non esiste, utilizzare null.

Penso che stai pensando troppo a questo :)


Non avrei dovuto trovare quel numero (95%), è stato un errore, scusa. Quello che volevo dire era che gran parte delle richieste sono interessate ad entrambe le risorse contemporaneamente. Ma c'è ancora un numero rilevante di richieste che sono solo interessate Foo, e poiché ognuna Barè abbastanza grande in memoria (circa 3x-4x la dimensione di Foo), non voglio restituire un Barse un client non lo richiede esplicitamente.
ceran,

Di quanto stiamo parlando? Dubito che sta andando a fare che molto di una differenza di tempo di trasferimento, e preferisco un API pulita sopra la velocità
Nathan Merrill
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.