Quali sono le "migliori pratiche" per gestire le risorse correlate durante la progettazione dell'API REST?

2

Quando si progetta l'API REST, in genere inizio identificando la risorsa del mio sistema, per fare un esempio diciamo che gestiamo più blogs , ognuno con più posts , ciascuno con più comments .

L'API REST standard per un caso come questo sarebbe avere alcuni endpoint come questi:

/blogs //all blogs

/blogs/:blog_id/posts //all post of blog :blog_id

/blogs/:blog_id/posts/:post_id/comments //all comments of post :post_id

Questo è un modo molto semplice ed efficace per implementare la funzionalità REST di base, ma penso che ciò che viene dopo è molto meno chiaro.

1 - Qual è un modo corretto di esporre la possibilità per il cliente di chiedere un sottoinsieme di blog?

Una possibilità potrebbe essere:

/blogs?blog_id=:blog_id1&blog_id=:blog_id2

Che cosa ne pensi?

2 - Qual è il modo corretto di esporre la possibilità per il cliente di chiedere un blog con i suoi post tutto in una sola richiesta?

Una possibilità potrebbe essere:

/blogs?_with=posts

Che cosa ne pensi?

3 - Se ti piace l'approccio alla domanda numero 2, che dire delle risorse secondarie delle risorse secondarie?

/blogs?_with=posts.comments

È accettabile? Quanti elementi annidati ha senso richiedere in questo modo?

Che dire

/blogs?_with=posts.comments.authors.posts.comments

C'è qualche ragione obiettiva (dal punto di vista del design, non sto parlando di cose come le prestazioni) per considerare questo tipo di endpoint male progettato .

Capisco che questa domanda sia per lo più soggettiva, ma penso che ci siano alcune "migliori pratiche" che possono essere identificate e implementate in un'ampia varietà di sistemi.

    
posta heapOverflow 22.03.2018 - 18:18
fonte

3 risposte

2

Filtri

I parametri di query sono una buona misura semantica per questo, e molte API sono implementate in questo modo, si veda ad esempio API OpenStack . Utilizzando i parametri di query in cui ogni filtro associa un singolo valore direttamente a un singolo attributo, è facile combinare i filtri per o che rappresentano un'intersezione logica tra due set di risultati (AND) o a unione logica (OR), ma non entrambi allo stesso tempo. Per esempio. la seguente richiesta può essere interpretata in due modi:

GET /blogs/:blog_id/posts?created_at=today&author=12

La richiesta può essere sia dicendo: Dammi tutti i post per il blog dato che sono stati creati oggi e il cui ID autore è 12, o si può dire: Dammi tutti i post per il blog dato che sono stati creati oggi o il cui ID autore è 12. Si può presumere che sia usato convenzione all'interno della tua specifica API, credo che la maggior parte delle persone creda che il carattere e commerciale preferisca l'approccio AND, ma non c'è motivo per cui non possa essere un'unione dei due set.

Potrebbe essere necessario interrogare risorse per cui è necessaria una logica più complessa per determinare quali risultati si formano. In questo caso devi essere un po 'più intelligente nel modo in cui imposti i parametri di query per i filtri e probabilmente avrai bisogno di qualche forma di linguaggio di query da implementare. I parametri effettivi nell'URL non verrebbero mappati agli attributi delle tue risorse, ma i valori contengono invece riferimenti agli attributi, insieme ad altre cose assortite che le lingue hanno come operatori come ">", "<" e parole chiave come AND e OR. Vedi ad esempio JIRA JQL :

GET /blogs/:blog_id/posts?query=created_at IS today AND (author IS 1 OR author IS 2)

Gli approcci possono essere combinati, in modo da poter disporre di un mapping uno-a-uno per i parametri / gli attributi di query e tuttavia supportare ancora un filtraggio più efficace:

GET /blogs/:blog_id/posts?created_at=today&author=in:1,2

Da un punto di vista concettuale non credo ci sia molta differenza tra l'utilizzo dei parametri di query e l'aggiunta di ulteriori stringhe sul percorso. Potrei semplicemente rappresentare la prima richiesta in questo modo:

GET /blogs/:blog_id/posts/created_at/today/author/12

Ho la sensazione però che supportare questo approccio sarebbe un po 'più difficile per abbinare le rotte contro contro solo usando i parametri di query e per determinare se una determinata sezione del percorso è un' attributo 'o un' valore ', ma il tuo il chilometraggio può variare. Ne parlo principalmente perché potresti volere che alcuni filtri più fondamentali siano effettivamente rappresentati sul percorso per un facile riconoscimento / facilità d'uso:

GET /blogs/:blog_id/posts/new

Tale richiesta potrebbe restituire gli stessi risultati di questa richiesta:

GET /blogs/:blog_id/posts?created_at=today

Dati incorporati

Esistono sistemi che fanno esattamente ciò che stai suggerendo. Vedi ad esempio campionato / frattale . Lo fanno un po 'più lontano, in cui è possibile passare parametri ai dati incorporati per filtrarli (che può essere combinato con gli approcci dall'alto). Ciò significa che puoi effettuare richieste come:

GET /blogs?include=posts.comments:created(today)

Questa richiesta restituirà tutti i blog, i loro post e solo i commenti creati "oggi" (rispetto a tutti i commenti su tutti i post).

Le dimensioni del tuo set di risorse potrebbero avere un fattore limitante su quanto sia reattiva la tua API, quindi se hai una risorsa di livello superiore con una catena di 40 risorse correlate al di sotto di essa potresti aver bisogno di fare attenzione nelle tue richieste di mantenere da sovraccaricare il tuo server delle risorse o fornire un limite massimo alla profondità della catena di relazioni che puoi percorrere.

Ci sono anche preoccupazioni sulle implicazioni per l'impaginazione. Se stai impaginando la risorsa di livello superiore che stai richiedendo, impagisci anche le risorse incorporate? Il supporto delle HATEOAS relazioni di collegamento in stile può aiutare il cliente a ottenere riferimenti per un'ulteriore immersione nella lista dei dati incorporati impaginati che potrebbe essere interessata in e può mitigare il problema, ma è un fattore da prendere in considerazione.

    
risposta data 22.03.2018 - 22:27
fonte
1
  1. Ci sono tre opzioni che ho visto. Il primo è quello di fare ciò che si propone, che nella maggior parte dei casi si tradurrà in ottenere un Long[] o somesuch sul lato server. Il secondo non è proprio un linguaggio di query: è sufficiente consentire una lista delimitata di id come stringa, quindi scomporla sul server. Il terzo sarebbe il percorso completo del linguaggio di query, come proposto da @JeffLambert. Se non hai bisogno di un linguaggio di query, è meno carico mentale per tutti scegliere una delle altre opzioni. Se vuoi un URL hackerabile dall'uomo, allora un elenco delimitato da virgole funziona bene. Alcuni framework avranno un tempo facile rendendo più parametri di query con lo stesso nome per te.

  2. In genere l'ho visto come include=[foo, bar, baz.blop] , ma non c'è magia nel nominare il parametro della query include.

  3. Per quanto riguarda il numero di elementi a cui è opportuno fare riferimento, in generale sono d'accordo con @RobertHarvey per essere guidato dalla legge di Demeter. Permetterei qualsiasi numero di include ad una profondità di uno, ad es. include=[foo, bar, baz, blort, ...] ma no include=[baz.blop] . Naturalmente, ciò dipende dalle esigenze dei tuoi utenti. Potrei escogitare un'applicazione critica dal punto di vista delle prestazioni in cui le vite sono in linea e hai bisogno di nidificare a profondità 12 in una query. Questa dovrebbe essere l'eccezione, non la regola. Il Web è più performante di quanto la maggior parte delle persone gli attribuisce il merito e, di solito, le query multiple vanno bene.

In un mondo ideale, direi che non ti preoccupare affatto del parametro include e metti i link fuori dall'oggetto base su foo, bar, baz, ecc. Assicurati di avere un vero, misurato problema di prestazioni con più query prima di avviare il percorso include .

    
risposta data 23.03.2018 - 13:10
fonte
-3

REST non si cura di quale ortografia si usa per gli identificatori di risorse

What is a proper way to expose the possibility for the client to ask for a subset of blogs?

Come faresti con un sito web?

What is the proper way to expose the possibility for the client to ask for a a blog with its posts all in one request?

Come faresti con un sito web?

If you like the approach at question number 2, what about sub-resources of sub-resources?

Come faresti con un sito web?

Una volta capito come farlo con un sito web, prendi lo stesso design e pensa a come rendere leggibili le pagine web in quel sito.

I'm not really sure if the website is a good comparison

Potresti voler esaminare, per esempio, la discussione di Asbjørn Ulsberg sulla Strategia di cambiamento dell'API

In many ways, a web API has more in common with a web site than to a statically linked software library.

    
risposta data 23.03.2018 - 04:17
fonte

Leggi altre domande sui tag