URL REST nidificati e ID genitore, qual è il design migliore?


20

Bene, abbiamo due risorse: Albume Song. Ecco l'API:

GET,POST /albums
GET,POST /albums/:albumId
GET,POST /albums/:albumId/songs
GET,POST /albums/:albumId/songs/:songId

Sappiamo che odiamo qualche canzone, si chiama Susy, per esempio. Dove dovremmo searchagire?

Un'altra domanda. Va bene, ora è più reale. Apriamo l'album 1 e cariciamo tutti i brani. Creiamo oggetti JS, ognuno contiene i dati dei brani e ha alcuni metodi come questo: remove, update.

L'oggetto della canzone ha ID, nome e cose, ma non ha idea di quale genitore appartenga, perché recuperiamo l'elenco delle canzoni per query e non sarà così buono restituire gli ID genitore con ciascuno di essi. Ho sbagliato?

Quindi vedo poche soluzioni, ma non ne sono sicuro.

  1. Rendi l'id parent facoltativo - come parametro get. Questo approccio che uso attualmente, ma penso che sia brutto.

    List,Create /songs?album=albumId
    Update,Delete /songs/:songId
    Get /songs/?name=susy # also, solution for first question
    
  2. Ibrido. Ora è così utile poiché abbiamo bisogno dell'ID album per eseguire OPTIONSquery per ottenere metadati.

    List,Create /album/:albumId/songs
    Update,Delete /songs/:songId
    POST /songs/search # also, solution for first question
    
  3. Restituisce l'URL completo con ogni istanza di risorsa. L'API è la stessa, ma avremo canzoni come questa:

    id: 5
    name: 'Elegy'
    url: /albums/2/songs/5
    

    Ho sentito che questo approccio si chiama HATEOAS.

  4. Quindi ... Per fornire l'ID genitore

    id: 5
    name: 'Elegy'
    albumId: 2
    

Che è migliore? O forse sono stupido? Dai qualche consiglio, ragazzi!

Risposte:


31

Dove dovremmo mettere le azioni di ricerca?

In GET /search/:text. Ciò restituirà un array JSON contenente le corrispondenze, ogni corrispondenza contenente l'album a cui appartiene. Questo ha senso, perché il cliente potrebbe essere interessato non alla traccia stessa, ma all'intero album (immagina di essere alla ricerca di una canzone che, a tuo avviso, era nello stesso album di quella che ricordi il nome).

non sarà così buono restituire gli ID parent con ciascuno di essi. Ho sbagliato?

Le singole tracce possono contenere l'album. Ciò garantirà l'uniformità della rappresentazione della traccia se è possibile ottenere una traccia tramite un album o tramite la ricerca (nessun album qui).

Che è migliore?

Come precedentemente affermato, includere l'album ha senso. Mentre il terzo punto (con il relativo URI) può essere interessante in alcuni casi (non devi pensare al modo in cui dovrebbe essere formato l'URI), ha uno svantaggio di non fornire esplicitamente l'album. Il quarto punto corregge questo. Se vedi il vantaggio di avere l'URI relativo nella risposta, puoi combinare i punti 3 e 4.

O forse sono stupido?

Scegliere buoni URI non è un compito facile, soprattutto perché non esiste un'unica risposta corretta. Se sviluppi il client contemporaneamente all'API, potrebbe aiutarti a visualizzare meglio come utilizzare l'API. Detto questo, altre persone potrebbero preferire altri usi a cui non stavi pensando durante lo sviluppo dell'API.

Un aspetto che può essere problematico è il modo in cui organizzi i dati internamente, ovvero l'utilizzo di una gerarchia. Dal tuo commento, ti stai chiedendo a cosa dovrebbe contenere una risposta GET /artist/1/album/10/song/3/comment/23, che mostra una visione molto orientata agli alberi. Ciò può comportare alcuni problemi durante l'estensione del sistema in un secondo momento. Per esempio:

  • Cosa succede se una canzone non ha un album?
  • E se un album avesse più artisti?
  • Cosa succede se si desidera aggiungere una funzione che consenta di commentare gli album?
  • Cosa succede se ci dovrebbero essere commenti di commenti?
  • eccetera.

Questo è essenzialmente il problema che ho spiegato nel mio blog : una rappresentazione ad albero ha troppe limitazioni per essere efficacemente utilizzata in molti casi.

Cosa succede se distruggi la gerarchia? Vediamo.

  1. GET /albums/:albumIdrestituisce un JSON contenente le meta informazioni sull'album (come l'anno in cui è stato pubblicato o l'URI del JPEG che mostra la copertina dell'album) e una serie di tracce. Per esempio:

    GET /albums/151
    {
        "id": 151,
        "gid": "dbd3cec7-b927-423f-894b-742c4c7b54ce",
        "name": "Yellow Submarine",
        "year": 1969,
        "genre": "Psychedelic rock",
        "artists": ["John Lennon", "Paul McCartney", ...],
        "tracks": [
            {
                "id": 90224,
                "title": "Yellow Submarine",
                "length": "2:40"
            },
            {
                "id": 83192,
                "title": "Only a Northern Song",
                "length": "3:24"
            }
            ...
        ]
    }

    Perché includo, ad esempio, la lunghezza di ciascuna traccia? Perché immagino che il cliente che mostra un album possa essere interessato elencando le tracce per titolo, ma anche mostrare la lunghezza di ciascuna traccia, la maggior parte dei clienti lo fa. D'altra parte, potrei non mostrare il compositore o gli artisti per ogni traccia, perché decido che queste informazioni non sono necessarie a questo livello. Ovviamente, le tue scelte potrebbero essere diverse.

  2. GET /tracks/:trackIdrestituisce le informazioni su una traccia specifica. Poiché non esiste più una gerarchia, non è necessario indovinare l'album o l'artista: l'unica cosa che devi sapere è l'identificatore della traccia stessa.

    O forse no? E se fosse possibile specificarlo per nome con GET /tracks/:trackName?

    GET /tracks/Only%20a%20Northern%20Song
    {
        "id": 83192,
        "gid": "8d9c4311-9d7b-40a4-8aeb-4fe96247fe2b",
        "title": "Only a Northern Song",
        "writers": ["George Harrison"],
        "artists": ["John Lennon", "Paul McCartney", "Ringo Starr"],
        "length": "3:24",
        "record-date": 1967,
        "albums": [151, 164],
        "soundtrack": {
            "uri": "http://audio.example.com/tracks/static/83192.mp3",
            "alias": "Beatles - Only a Northern Song.mp3",
            "length-bytes": 3524667,
            "allow-streaming": true,
            "allow-download": false
        }
    }

    Ora guarda più da vicino albums; cosa vedi? Giusto, non uno, ma due album. Se hai una gerarchia, non puoi farlo (a meno che non duplichi il record).

  3. GET /comments/:objectGid. Potresti aver individuato i brutti GUID nelle risposte. Questi GUID consentono di identificare l'entità nel database al fine di eseguire attività che possono essere applicate ad album, artisti o brani. Come commentare.

    GET /comments/8d9c4311-9d7b-40a4-8aeb-4fe96247fe2b
    [
        {
            "author": {
                "id": 509931,
                "display-name": "Arseni Mourzenko"
            },
            "text": "What a great song! (And I'm proud of the usefulness of my comment)",
            "concerned-object": "/tracks/83192"
        }
    ]

    Il commento fa riferimento all'oggetto in questione, rendendo possibile accedervi quando si accede al commento al di fuori del suo contesto (ad esempio quando si moderano gli ultimi commenti GET /comments/latest).

Nota che questo non significa che dovresti evitare qualsiasi forma di gerarchia nella tua API. Ci sono casi in cui ha senso. Come regola generale:

  • Se la risorsa non ha senso al di fuori del contesto della sua risorsa padre, utilizzare la gerarchia.

  • Se la risorsa può vivere (1) da sola o (2) in un contesto di risorse padre di tipi diversi o (3) avere più genitori, la gerarchia non deve essere utilizzata.

Ad esempio, le righe di un file non hanno senso al di fuori del contesto di un file, quindi:

GET /file/:fileId

e:

GET /file/:fileId/line/:lineIndex

stanno bene.


Sì, dalla ricerca, posso restituire anche tutte le informazioni sull'album, creerà un'altra risorsa - SongSearchResult, va bene, suppongo. Ma che dire degli URL? Devo fornire parentIDogni oggetto e usarlo come parametro GET o parte normale dell'URL? E se avessi profondità> 2? /artist/1/album/10/song/3/comment/23- è folle fornire ogni id di artista, album e canzone commentnell'oggetto, ma ho sentito che è una strada da percorrere, ma non è disgustoso ?!
dt0xff

@ dt0xff: ho modificato la mia risposta. Penso che ora dovrebbe darti un'immagine chiara di come la profondità può essere evitata.
Arseni Mourzenko,

Sì, è chiaro ora che è molto più facile implementare il punto di ingresso per ogni risorsa (tranne alcune come linea o altra cosa funzionale) senza aggiungerlo a parent tramite url. Grazie, mi hai convinto che la mia scelta era giusta e che "l'approccio comune" (davvero, molte cose nidificate .. restangularè costruito su di esso) non è così buono.
dt0xff

Bella risposta. Ho alcune obiezioni però. "E se un album avesse più artisti?" URI diversi possono identificare la stessa risorsa poiché l'URI della relazione binaria -> risorsa è univoco a destra (molti-a-uno). Quindi gli URI /artists/foo/albums/quxe /artists/bar/albums/quxpossono identificare perfettamente la stessa risorsa di album. In altre parole, il componente path in un URI rappresenta una gerarchia di grafici , non necessariamente una gerarchia ad albero , che lo rende adatto a rappresentare non solo le categorie ma anche i tag.
Maggyero

1
... "Questo è essenzialmente il problema che ho spiegato nel mio blog : una rappresentazione ad albero ha troppe limitazioni per essere efficacemente utilizzata in molti casi." Quindi no, questo non è un problema. "Cosa succede se si desidera aggiungere una funzione che consente di commentare gli album?" Questo non è un problema: /artists/foo/albums/qux/comments/7. "Cosa succede se ci dovrebbero essere commenti di commenti?" Analogamente: /artists/foo/albums/qux/song/5/comments/2/comments/8.
Maggyero
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.