Alternative RESTful a DELETE Request Body


93

Anche se la specifica HTTP 1.1 sembra consentire il corpo dei messaggi sulle richieste DELETE , sembra indicare che i server dovrebbero ignorarla poiché non ci sono semantiche definite per esso.

4.3 Corpo del messaggio

Un server DOVREBBE leggere e inoltrare il corpo del messaggio su qualsiasi richiesta; se il metodo di richiesta non include la semantica definita per un corpo di entità, il corpo del messaggio DOVREBBE essere ignorato durante la gestione della richiesta.

Ho già esaminato diverse discussioni correlate su questo argomento su SO e oltre, come ad esempio:

La maggior parte delle discussioni sembra concordare sul fatto che fornire un corpo del messaggio su un DELETE può essere consentito , ma generalmente non è raccomandato.

Inoltre, ho notato una tendenza in varie librerie client HTTP in cui sembrano essere registrati sempre più miglioramenti per queste librerie per supportare i corpi delle richieste su DELETE. La maggior parte delle biblioteche sembra accontentarsi, anche se occasionalmente con un po 'di resistenza iniziale.

Il mio caso d'uso richiede l'aggiunta di alcuni metadati obbligatori su un DELETE (ad esempio il "motivo" per l'eliminazione, insieme ad alcuni altri metadati richiesti per l'eliminazione). Ho considerato le seguenti opzioni, nessuna delle quali sembra completamente appropriata e in linea con le specifiche HTTP e / o le migliori pratiche REST:

  • Corpo del messaggio - La specifica indica che il corpo del messaggio su DELETE non ha valore semantico; non completamente supportato dai client HTTP; non una pratica standard
  • Intestazioni HTTP personalizzate - La richiesta di intestazioni personalizzate è generalmente contro le pratiche standard ; il loro utilizzo non è coerente con il resto della mia API, nessuna delle quali richiede intestazioni personalizzate; inoltre, nessuna buona risposta HTTP disponibile per indicare valori di intestazione personalizzati errati (probabilmente una domanda a parte)
  • Intestazioni HTTP standard : nessuna intestazione standard è appropriata
  • Parametri di query : l'aggiunta di parametri di query modifica effettivamente l'URI della richiesta da eliminare; contro le pratiche standard
  • Metodo POST - (ad esempio POST /resourceToDelete { deletemetadata }) POST non è un'opzione semantica per l'eliminazione; POST rappresenta effettivamente l' azione opposta desiderata (ovvero POST crea risorse subordinate; ma ho bisogno di eliminare la risorsa)
  • Metodi multipli - La divisione della richiesta DELETE in due operazioni (ad es. PUT delete metadata, then DELETE) divide un'operazione atomica in due, lasciando potenzialmente uno stato incoerente. Il motivo dell'eliminazione (e altri metadati correlati) non fanno parte della rappresentazione della risorsa stessa.

La mia prima preferenza sarebbe probabilmente quella di utilizzare il corpo del messaggio, secondo alle intestazioni HTTP personalizzate; tuttavia, come indicato, ci sono alcuni aspetti negativi di questi approcci.

Esistono consigli o best practice in linea con gli standard REST / HTTP per l'inclusione di tali metadati obbligatori nelle richieste DELETE? Ci sono altre alternative che non ho considerato?


2
Alcune implementazioni come Jerseynon consentono il corpo per le deleterichieste.
basiljames

Risposte:


44

Nonostante alcuni consigli di non utilizzare il corpo del messaggio per le richieste DELETE, questo approccio può essere appropriato in alcuni casi d'uso. Questo è l'approccio che abbiamo utilizzato dopo aver valutato le altre opzioni menzionate nella domanda / risposte e dopo aver collaborato con i consumatori del servizio.

Anche se l'uso del corpo del messaggio non è l'ideale, nessuna delle altre opzioni era perfettamente adatta. Il corpo della richiesta DELETE ci ha permesso di aggiungere facilmente e chiaramente la semantica intorno ai dati / metadati aggiuntivi necessari per accompagnare l'operazione DELETE.

Sarei ancora aperto ad altri pensieri e discussioni, ma volevo chiudere il ciclo su questa domanda. Apprezzo i pensieri e le discussioni di tutti su questo argomento!


12
Questa è una cattiva idea. Un punto in cui questo ti metterà nei guai è se in seguito decidi di utilizzare un servizio di accelerazione HTTP come Akamai EdgeConnect. So per certo che EdgeConnect rimuove i corpi dalle richieste HTTP DELETE (poiché consumano larghezza di banda probabilmente non sono validi). È anche probabile che servizi simili facciano lo stesso (vedi la funzione di accelerazione di Kindle e altri servizi simili a CDN). Probabilmente dovresti riprogettare per non utilizzare i verbi HTTP per il tuo servizio. La maggior parte delle API ha poco senso utilizzando i verbi HTTP / REST classico e i problemi di trasporto dei verbi HTTP sono molto difficili da risolvere.
Gabe

3
Concordo con @Gabe, l'invio di un corpo con metodi che non hanno corpo per definizione è un modo infallibile per perdere dati casualmente mentre i tuoi bit attraversano i tubi di Internet, e avrai difficoltà a eseguirne il debug.
Nicholas Shanks

3
Questi commenti contro l'utilizzo di DELETE sono irrilevanti finché non affrontano le questioni molto valide che ha l'OP. Sto facendo del mio meglio per aderire allo spirito di REST, ma un'eliminazione senza concorrenza ottimistica e un'eliminazione che non ha un'operazione batch atomica non è pratica nelle situazioni della vita reale. Questa è una grave carenza del pattern REST.
Quarkly

Sono con @Quarkly su questo. Non capisco quale sia l'idea RESTFUL su come dovremmo controllare la concorrenza, ecc. I controlli di concorrenza non appartengono al client.
Dirk Wessels

13

Quello che sembri desiderare è una delle due cose, nessuna delle quali è pura DELETE:

  1. Hai due operazioni, una PUTdel motivo di cancellazione seguita da una DELETEdella risorsa. Una volta eliminati, i contenuti della risorsa non sono più accessibili a nessuno. Il "motivo" non può contenere un collegamento ipertestuale alla risorsa eliminata. O,
  2. Stai tentando di modificare una risorsa da state=activea state=deletedutilizzando il DELETEmetodo. Le risorse con stato = deleted vengono ignorate dall'API principale ma potrebbero comunque essere leggibili da un amministratore o da qualcuno con accesso al database. Ciò è consentito: DELETEnon è necessario cancellare i dati di supporto per una risorsa, solo per rimuovere la risorsa esposta in quell'URI.

Qualsiasi operazione che richiede un corpo del messaggio su una DELETErichiesta può essere suddivisa nel modo più generale, a POSTper eseguire tutte le attività necessarie con il corpo del messaggio e a DELETE. Non vedo motivo per rompere la semantica di HTTP.


2
Cosa succede se la PUTragione ha successo e le DELETErisorse falliscono? Come si può prevenire lo stato incoerente?
Lightman

1
@ Lightman il PUT specifica solo l'intento. Può esistere senza un DELETE corrispondente, che indicherebbe che qualcuno voleva eliminare ma non è riuscito o ha cambiato idea. L'inversione dell'ordine delle chiamate consentirebbe anche che le DELETE si verifichino senza motivo: la fornitura di una ragione sarebbe quindi considerata semplicemente come annotazione. È per entrambi questi motivi che consiglierei di utilizzare l'opzione 2 di cui sopra, ovvero cambiare lo stato del record sottostante in modo da far scomparire la risorsa HTTP dall'URL corrente. Un garbage collector / admin può quindi eliminare i record
Nicholas Shanks

7

Data la situazione che hai, adotterei uno dei seguenti approcci:

  • Invia un PUT o PATCH : sto deducendo che l'operazione di eliminazione è virtuale, per la natura della necessità di un motivo di eliminazione. Pertanto, credo che l'aggiornamento del record tramite un'operazione PUT / PATCH sia un approccio valido, anche se non è un'operazione DELETE di per sé.
  • Utilizza i parametri della query : l'URI della risorsa non viene modificato. In realtà penso che anche questo sia un approccio valido. La domanda che hai collegato parlava di non consentire l'eliminazione se il parametro della query era mancante. Nel tuo caso, avrei solo un motivo predefinito se il motivo non è specificato nella stringa di query. La risorsa sarà ancora resource/:id. Puoi renderlo rilevabile con le intestazioni dei link sulla risorsa per ogni motivo (con un reltag su ciascuna per identificare il motivo).
  • Usa un endpoint separato per motivo : utilizzando un URL come resource/:id/canceled. Questo in realtà cambia l'URI della richiesta e sicuramente non è RESTful. Anche in questo caso, le intestazioni dei collegamenti possono renderlo rilevabile.

Ricorda che REST non è una legge o un dogma. Consideralo più come una guida. Quindi, quando ha senso non seguire le indicazioni per il dominio problematico, non farlo. Assicurati solo che i tuoi consumatori API siano informati della varianza.


Per quanto riguarda l'uso dei parametri di query, la mia comprensione è che la query fa parte dell'URI della richiesta per la sezione 3.2 , e quindi l'utilizzo di questo approccio (o allo stesso modo, gli endpoint separati) va contro la definizione del metodo DELETE , in modo tale che "risorsa identificato dall'URI della richiesta "viene eliminato.
shelley

La risorsa è identificata dal percorso uri. Quindi un GET to /orders/:idrestituirà la stessa risorsa di /orders/:id?exclude=orderdetails. La stringa di query fornisce solo suggerimenti al server, in questo caso per escludere i dettagli dell'ordine nella risposta (se supportati). Allo stesso modo, se stai inviando DELETE a /orders/:ido /orders/:id?reason=canceledo /orders/:id?reason=bad_credit, stai ancora agendo sulla stessa risorsa sottostante. Per mantenere un '"interfaccia uniforme", avrei un motivo predefinito in modo che non sia necessario inviare il parametro della query.
codeprogressione

@shelley Hai ragione nelle tue preoccupazioni sulle stringhe di query. La stringa di query fa parte dell'URI. L'invio di una richiesta DELETE a /foo?123significa che stai eliminando una risorsa diversa rispetto a se dovessi inviare DELETE /foo?456.
Nicholas Shanks

@codeprogression Scusa, ma molto di quello che dici è sbagliato. La risorsa è identificata dall'intero URI, non solo dal percorso. Stringhe di query diverse sono risorse diverse (nel senso HTTP della parola "risorsa"). Inoltre, non è richiesto un motivo predefinito per un'interfaccia uniforme. Questo termine si riferisce all'uso di GET, PUT, POST, PATCH e DELETE nel modo in cui HTTP li ha definiti. La comunanza è tra i fornitori (fornitori di user agent, fornitori di API, fornitori di proxy di cache, ISP, ecc.) E non all'interno della propria API (anche se anche questa dovrebbe essere uniforme nel design per la sanità mentale dei suoi utenti!).
Nicholas Shanks

@Nicholas Non capisco la tua necessità di sostenere una discussione terminata tre anni fa. La risposta e i commenti che ho fatto sono validi e corretti da una vista incentrata sul REST. REST non è HTTP (né alcuna implementazione del fornitore di HTTP). Nel contesto di REST, le risorse sono le stesse. E come ho detto nella mia risposta, REST non è legge o dogma, ma guida.
codeprogressione

0

Ti suggerisco di includere i metadati richiesti come parte della gerarchia URI stessa. Un esempio (ingenuo):

Se è necessario eliminare le voci in base a un intervallo di date, invece di passare la data di inizio e la data di fine nel corpo o come parametri di query, strutturare l'URI in modo da passare le informazioni richieste come parte dell'URI.

per esempio

DELETE /entries/range/01012012/31122012 - Elimina tutte le voci tra il 1 ° gennaio 2012 e il 31 dicembre 2012

Spero che questo ti aiuti.


5
Non copre casi come l'invio di un motivo per l'eliminazione, ad esempio il campo dei commenti.
Kugel

3
Wow. questa è un'idea terribile. Se si dispone di una quantità eccessiva di metadati, aumenterà le limitazioni di dimensione sull'URI.
Balaji Boggaram Ramanarayan

1
Questo approccio non segue le pratiche RESTful e non è consigliato poiché avrai una struttura URI contorta. Gli endpoint vengono confusi con l'identificazione delle risorse intrecciate rispetto ai metadati e nel tempo diventeranno un incubo di manutenzione man mano che la tua API cambia. È molto più preferito avere i rangeparametri della query o il payload specificati che è la carne di questa domanda: capire l'approccio delle migliori pratiche al problema che direi non è questo.
digitaldreamer

@digitaldreamer - Non capisco cosa intendi per struttura URI convoluta? Inoltre, questo è un HTTP DELETE, quindi il payload non è un'opzione ma i parametri di query sì.
Suresh Kumar
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.