API REST: l'API deve restituire oggetti JSON nidificati?


38

Quando si tratta di API JSON è buona norma appiattire le risposte ed evitare oggetti JSON nidificati?

Ad esempio, diciamo che abbiamo un'API simile a IMDb ma per i videogiochi. Esistono un paio di entità, Game, Platform, ESRBRating e GamePlatformMap che mappano giochi e piattaforme.

Diciamo che richiedi / game / 1 che recupera il gioco con ID 1 e restituisce l'oggetto di gioco con le piattaforme ed esrbRating nidificato.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": [
    {"id":1,"name":"Xbox"},
    {"id":2,"name":"Playstation"}
  ],
  "esrbRating": {
    "id": 1,
    "code": "E",
    "name": "Everyone"
  }
}

Se stai usando qualcosa come JPA / Hibernate, potrebbe farlo automaticamente se impostato su FETCH.EAGER.

L'altra opzione è semplicemente l'API e aggiungere più punti finali.

In tal caso quando viene richiesto / game / 1 viene restituito solo l'oggetto del gioco.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
}

Se vuoi le piattaforme e / o ESRBRating dovresti chiamare il seguente:

/ game / 1 / platform / game / 1 / esrb

Questo metodo sembra che potrebbe potenzialmente aggiungere molte più chiamate al server a seconda dei dati di cui il client ha bisogno e quando ne hanno bisogno.

Ci fu un ultimo pensiero che avevo dove avresti restituito qualcosa del genere.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": ["Xbox","Playstation"]
}

Tuttavia, ciò presuppone che non siano necessari gli ID o qualsiasi altra informazione possa essere associata a tali oggetti della piattaforma.

Chiedo in generale qual è il modo migliore per strutturare gli oggetti JSON restituiti dall'API. Dovresti cercare di stare il più vicino possibile alle tue entità, o è bene usare oggetti di dominio o oggetti di trasferimento dati? Comprendo che i metodi avranno dei compromessi, o più lavoro sul livello di accesso ai dati o più lavoro per il cliente.

Vorrei anche sentire una risposta relativa all'uso di Spring MVC come tecnologia di back-end per l'API, con JPA / Hibernate o MyBatis per la persistenza.


6
Quali obiezioni, se presenti, hai restituendo oggetti incorporati? Restituire oggetti incorporati singolarmente da endpoint diversi diventerà dannatamente fastidioso (per non parlare lento).
Robert Harvey,

1
Personalmente non ho obiezioni. Non sono a conoscenza di quelle che sono considerate le migliori pratiche. Un collega afferma che lavorare con oggetti incorporati in AngularJS non è semplice e alla fine vorrei che l'app Ember of AngularJS consumasse l'API. Non so abbastanza su Angular o Ember per sapere se ciò avrà un impatto o meno.
greyfox,

3
La risposta dipenderà dal fatto se si desidera restituire oggetti di dominio, DTO, oggetti ViewModel o oggetti KitchenSink. L'oggetto che stai restituendo sarà molto probabilmente determinato da ciò di cui l'applicazione ha bisogno e da come si comporta l'oggetto su Internet. Esempio: se stai provando a riempire una pagina web con i dati di una fattura, molto probabilmente restituirai un oggetto contenente tutto ciò di cui hai bisogno (a meno che tu non preveda AJAXing negli elementi pubblicitari o qualcosa del genere).
Robert Harvey,

Questo è il caso in cui quando si richiede un gioco è probabile che si desideri conoscere generi, piattaforme e ESRBRating. Ciò ha senso. In termini di progettazione da una prospettiva Java, consiglieresti di avere un pacchetto Entity con JPA entit, e quindi un pacchetto di dominio che gli oggetti business / DTO vengano restituiti all'utente?
greyfox,

1
Le chiamate al server sono costose. Un'API che richiede di inviare dati utilizzando più chiamate sarà più lenta di un'API che consente di ottenere tutto in una chiamata, spesso anche quando quest'ultima restituisce informazioni non necessarie.
Gort the Robot

Risposte:


11

Un'altra alternativa (usando HATEOAS). Questo è semplice, soprattutto nella pratica si aggiunge un tag link nel json a seconda dell'uso di HATEOAS.

http://api.example.com/games/1:

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": [
    {"_self": "http://api.example.com/games/1/platforms/53", "name": "Playstation"},
    {"_self": "http://api.example.com/games/1/platforms/34", "name": "Xbox"},
  ]
}

http://api.example.com/games/1/platforms/34:

{
  "id": 34,
  "title": "Xbox",
  "publisher": "Microsoft",
  "releaseDate": "2015-01-01",
  "testReport": "http://api.example.com/games/1/platforms/34/reports/84848.pdf",
  "forms": [
    {"type": "edit", "fields: [] },
  ]
}

Puoi ovviamente incorporare tutti i dati in tutti gli elenchi, ma probabilmente saranno troppi dati. In questo modo è possibile incorporare i dati richiesti e quindi caricarne di più se si desidera davvero utilizzarli.

L'implementazione tecnica può contenere la memorizzazione nella cache. Puoi memorizzare nella cache i collegamenti e i nomi delle piattaforme nell'oggetto di gioco e inviarlo all'istante senza dover caricare le piattaforme api. Quindi, quando necessario, è possibile caricarlo.

Vedi ad esempio che ho aggiunto alcune informazioni sul modulo. L'ho fatto per mostrarti che in un oggetto json dettagliato possono esserci molte più informazioni di quelle che vorresti caricare nell'elenco dei giochi.


Non penso che sia tecnicamente HATEOS poiché non esiste uno stato.
RibaldEddie,

Sì, non sono sicuro della parola esatta su questo processo. HATEOS in generale viene utilizzato per collegare le API di riposo, ma sono d'accordo che ha anche a che fare con lo stato. Sebbene l'idea di implementazione sarà la stessa. Qui puoi vedere un po 'di più su come può essere usato da un esempio: stormpath.com/blog/linking-and-resource-expansion-rest-api-tips
Luc Franken

È una buona idea però!
RibaldEddie,

1
Se stai sviluppando un'API in cui c'è coesione tra client e l'API stesso (ad esempio un api interno), potrebbe avere più senso restituire una risposta nidificata (o appiattita) anziché fornire collegamenti a un'altra risorsa, il che significa più richieste API che può essere indesiderato.
Bruno,

@bruno sì ma con un limite: su sistemi più grandi non puoi o non vuoi fornire per intero tutti gli oggetti correlati. I campi che includi per impostazione predefinita sono arbitrari, puoi selezionarli in base all'uso dell'API. Quindi in questo caso potresti avere piattaforme con centinaia di campi, il caso d'uso mostra una casella di selezione per scegliere una piattaforma. Quindi ha senso includere il nome della piattaforma ma non ha bisogno, ad esempio, dei dettagli finanziari della piattaforma.
Luc Franken,

16

Questa è una di quelle domande di base quando si tratta della progettazione dell'API REST. Ogni designer si pone questa domanda il primo giorno. Siamo spiacenti ma la risposta è "dipende". Ogni approccio ha pro e contro e dovrai solo prendere una decisione e seguirla.


11
Questo non è affatto utile. Lo stesso OP sapeva "dipende e ogni approccio ha pro e contro". Dovresti spiegare da cosa dipende o almeno fare un esempio.
Pratik Singhal,

5

Secondo, l'approccio presentato qui https://www.slideshare.net/stormpath/rest-jsonapis

In breve, includere la risorsa nidificata come collegamenti nella risorsa padre, nel frattempo fornire un parametro di espansione nell'endpoint padre.

Secondo me, questo è un modo che è efficiente e flessibile nella maggior parte dei casi.


2
Mi piace questo approccio. Per chiunque si chieda, questo inizia da SLIDE 57 nella presentazione collegata.
Adam Plocher,
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.