API RESTful: verbi HTTP con URL condivisi o specifici?


25

Durante la creazione di un'API RESTful , devo usare i verbi HTTP sullo stesso URL (quando è possibile) o devo creare un URL specifico per azione?

Per esempio:

GET     /items      # Read all items
GET     /items/:id  # Read one item
POST    /items      # Create a new item
PUT     /items/:id  # Update one item
DELETE  /items/:id  # Delete one item

O con URL specifici come:

GET     /items            # Read all items
GET     /item/:id         # Read one item
POST    /items/new        # Create a new item
PUT     /item/edit/:id    # Update one item
DELETE  /item/delete/:id  # Delete one item

Risposte:


46

Nel tuo ultimo schema, mantieni i verbi negli URL delle tue risorse. Questo dovrebbe essere evitato in quanto i verbi HTTP dovrebbero essere usati a tale scopo. Abbraccia il protocollo sottostante invece di ignorarlo, duplicarlo o sovrascriverlo.

Guarda DELETE /item/delete/:id, inserisci le stesse informazioni due volte nella stessa richiesta. Questo è superfluo e dovrebbe essere evitato. Personalmente, sarei confuso con questo. L'API supporta effettivamente le DELETErichieste? Cosa succede se inserisco deletel'URL e utilizzo invece un verbo HTTP diverso? Abbinerà qualcosa? In tal caso, quale sarà scelto? Come cliente di un'API correttamente progettata, non avrei dovuto porre tali domande.

Forse ne hai bisogno per supportare in qualche modo i client che non possono emettere DELETEo PUTrichieste. In tal caso, passerei queste informazioni in un'intestazione HTTP. Alcune API usano X-HTTP-Method-Overrideun'intestazione per questo scopo specifico (che penso sia abbastanza brutto comunque). Certamente non metterei i verbi nei percorsi.

Andare per

GET     /items      # Read all items
GET     /items/:id  # Read one item
POST    /items      # Create a new item
PUT     /items/:id  # Update one item
DELETE  /items/:id  # Delete one item

La cosa importante dei verbi è che sono già ben definiti nella specifica HTTP e il fatto di essere in linea con queste regole consente di utilizzare cache, proxy e possibilmente altri strumenti esterni all'applicazione che comprendono la semantica dell'HTTP ma non la semantica dell'applicazione . Tieni presente che il motivo per cui dovresti evitare di averli nei tuoi URL non riguarda le API RESTful che richiedono URL leggibili. Si tratta di evitare inutili ambiguità.

Inoltre, un'API RESTful può mappare questi verbi (o qualsiasi loro sottoinsieme) a qualsiasi set di semantica dell'applicazione, purché non vada contro le specifiche HTTP. Ad esempio, è perfettamente possibile costruire un'API RESTful che solo usi richieste GET, se tutte le operazioni che permette siano sia sicuri e idempotente . La mappatura sopra è solo un esempio che si adatta al tuo caso d'uso ed è conforme alle specifiche. Non deve necessariamente essere così.

Tieni inoltre presente che un'API veramente RESTful non dovrebbe mai richiedere a un programmatore di leggere un'ampia documentazione degli URL disponibili, purché tu sia conforme al principio HATEOAS (Hypertext as the Engine of Application State), che è uno dei presupposti fondamentali di REST . I collegamenti possono essere assolutamente incomprensibili per gli esseri umani purché l'applicazione client possa comprenderli e utilizzarli per capire le possibili transizioni dello stato dell'applicazione.


4
In assenza di PUTe DELETE, preferirei aggiungerlo al percorso, non differenziarlo con una stringa di query. Non è una modifica della stringa di query a un'operazione esistente; è un'operazione separata.
Robert Harvey,

4
@RobertHarvey in questo caso, lo definirei comunque un trucco. Come dici tu, è un'operazione e non è qualcosa che avrei messo nel percorso quando progettavo un'API che mira ad essere RESTful. Inserendolo nella stringa di query sembra solo meno invasivo. Impedisce la memorizzazione nella cache, ma non credo che le risposte a questo tipo di richieste debbano essere memorizzate nella cache. Consente inoltre al consumatore dell'API di indicare facilmente il metodo senza analizzare o costruire l'URL. Idealmente, un'API veramente RESTful dovrebbe fornire i collegamenti ipertestuali senza richiedere ai client di creare autonomamente gli URL.
toniedzwiedz,

Se non hai tutti i verbi, non è comunque completamente RESTful, vero?
Robert Harvey,

@RobertHarvey è vero, ma li tratto come un fallback, non come il progetto previsto. Immagino che l'API dovrebbe supportare i metodi HTTP attuali e se alcuni client non possono implementarli per nessun motivo, possono semplicemente sostituire il loro utilizzo con questi parametri di query. Un proxy potrebbe persino catturarli al volo e trasformare le richieste in richieste usando verbi HTTP autentici in modo che il server non debba nemmeno preoccuparsene. Poche API in giro sono veramente RESTful. Quando si tratta di API Web generiche, è davvero una questione di gusti. Personalmente, sceglierei URL puliti. Più facile da capire IMHO.
toniedzwiedz,

1
@RobertHarvey come spiegato, non è certo il modo previsto per usarli. Trovo solo questo il minore dei due mali per quando devi superare le limitazioni del cliente. Ricordo di aver letto una documentazione per tale API, ma dovrò fare degli scavi nella cronologia / nei segnalibri del mio browser per trovarla. Ora che ci penso, un'intestazione potrebbe essere migliore in questo caso. Sei d'accordo?
toniedzwiedz,

14

Il primo.

Un URI / URL è un identificatore di risorsa (suggerimento nel nome: identificatore di risorsa uniforme). Con la prima convenzione, la risorsa di cui si parla quando si fa "GET / user / 123" e la risorsa di cui si parla quando si fa "ELIMINA / utente / 123" è chiaramente la stessa risorsa perché hanno lo stesso URL.

Con la seconda convenzione, non puoi essere sicuro che "GET / user / 123" e "DELETE / user / delete / 123" siano effettivamente la stessa risorsa e sembra implicare che stai eliminando una risorsa correlata anziché la risorsa stesso, quindi sarebbe piuttosto sorprendente che l'eliminazione /user/delete/123effettivamente elimini /user/123. Se tutte le operazioni funzionano su URL diversi, l'URI non funziona più come identificatore di risorse.

Quando dici DELETE /user/123, stai dicendo "elimina" record utente con ID 123 "". Mentre se dici DELETE /user/delete/123, ciò che sembra implicare è "eliminare 'il record di eliminazione utente con ID 123'", che probabilmente non è quello che vuoi dire. E anche se usi il verbo più corretto in questa situazione: "POST / user / delete / 123" che dice "esegui l'operazione allegata a 'user deletor with id 123'", è ancora un modo per dire di eliminare un record (questo è simile alla definizione di un verbo in inglese).

Un modo in cui puoi pensare all'URL è di trattarlo come puntatore a oggetti e risorse come oggetti nella programmazione orientata agli oggetti. Quando si esegue GET /user/123, DELETE /user/123, si può pensare pensare a loro come metodi nell'oggetto: [/user/123].get(), [/user/123].delete()dove la []è come un puntatore dereferenziazione operatore, ma per gli URL (se si conosce un linguaggio che hanno puntatori). Uno dei principi di base di REST è un'interfaccia uniforme, ovvero avere un insieme piccolo e limitato di verbi / metodi che funziona per tutto in una vasta rete di risorse / oggetti.

Pertanto, il primo è migliore.

PS: certo, questo sta guardando REST nel modo più puro. A volte la praticità batte la purezza ed è necessario fare concessioni per clienti o strutture cerebrali che rendono difficile il corretto REST.


+1 per l'esempio OOP :)
53777A

6

(scusate, la prima volta che ho perso il / edit / e / delete / in (2) ...)

L'idea dell'URI è che si tratta di un identificatore di una risorsa indirizzabile , piuttosto che di una chiamata al metodo . Quindi l'URI dovrebbe puntare a una risorsa specifica. E se rinvii l'URI, dovresti sempre ottenere la stessa risorsa.

Ossia, dovresti pensare agli URI nello stesso modo in cui pensi alla Chiave primaria di una riga in un database. Identifica in modo univoco qualcosa: Universal Resource Identifier.

Quindi, indipendentemente dal fatto che tu usi il plurale o il singolare, l'URI dovrebbe essere un identificatore anziché un'invocazione . Quello che stai cercando di fare va nel metodo, vale a dire: GET (get), PUT (create / update), DELETE (delete) o POST (tutto il resto).

Quindi "/ item / delete / 123" interrompe REST perché non punta a una risorsa, è più una chiamata al metodo.

(Inoltre, solo semanticamente, dovresti essere in grado di OTTENERE un URI, decidere che è obsoleto e quindi ELIMINARE lo stesso URI, perché è un identificatore. Se l'URI GET non ha "/ delete /" e DELETE sì, allora questo va contro la semantica HTTP. Stai trasmettendo 2 o più URI per risorsa dove 1 farà.)

Ora, il trucco è questo: non esiste una vera definizione chiara di ciò che è e non è una risorsa, quindi la schivata comune in REST è definire un "sostantivo di elaborazione" e puntare l'URI a quello. È praticamente un gioco di parole, ma soddisfa la semantica.

Quindi se, per esempio, non potessi davvero usarlo per qualche motivo:

DELETE /items/123

potresti dichiarare al mondo che hai una "elaborazione" di risorse di elaborazione e utilizzo

POST /items/deletor  { id: 123 }

Ora, assomiglia molto a RPC (Remote Procedure Call), ma cade attraverso l'enorme lacuna della clausola di "elaborazione dei dati" della specifica POST denominata nella specifica HTTP.

Tuttavia, farlo è un po 'eccezionale, e se puoi usare il PUT comune per creare / aggiornare, DELETE per eliminare e POST per aggiungere, creare e tutto il resto, allora dovresti , perché è un uso più standard di HTTP. Ma se hai un caso complicato come "commit" o "publishing" o "redact", allora il caso di usare un nome di processore soddisfa i puristi REST e ti dà ancora la semantica di cui hai bisogno.

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.