2013-01-14 10 views
69

Mentre il consente di inviare corpi di messaggi sulle richieste DELETE, sembra indicare che i server devono ignorarlo poiché non esiste una semantica definita per esso.Alternative RESTful per ELIMINARE l'ente di richiesta

4,3 Corpo del messaggio

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

Ho già recensito diverse discussioni correlate su questo argomento su SO e al di là, come ad esempio:

maggior parte delle discussioni sembrano concordo che fornire un corpo del messaggio su DELETE può essere consentito, ma in genere non è raccomandato.

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

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

  • corpo del messaggio - La specifica indica che il corpo dei messaggi su DELETE non hanno alcun valore semantico; non completamente supportato dai client HTTP; non pratica standard
  • Intestazioni HTTP personalizzate - La richiesta di intestazioni personalizzate è generalmente against standard practices; utilizzarli è incoerente con il resto della mia API, nessuno dei quali richiede intestazioni personalizzate; inoltre, nessuna buona risposta HTTP a disposizione per indicare i valori di intestazione male personalizzati (probabilmente una questione a parte tutto)
  • standard HTTP intestazioni - Nessuna testata standard sono appropriati
  • parametri di query - Aggiunta di query params in realtà cambia la richiesta- URI cancellato; against standard practices
  • Metodo POST - (ad esempio POST /resourceToDelete { deletemetadata }) Il POST non è un'opzione semantica per l'eliminazione; POST rappresenta in realtà il opposta azione desiderata (ad esempio POST crea subordinati di risorse, ma ho bisogno di eliminare la risorsa)
  • multipli Metodi - Splitting la richiesta di eliminazione in due operazioni (ad esempio PUT eliminare i metadati, quindi eliminare) Split un operazione atomica in due, potenzialmente lasciando uno stato incoerente.Il motivo di 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, in secondo luogo alle intestazioni HTTP personalizzate; tuttavia, come indicato, ci sono alcuni aspetti negativi di questi approcci.

Esistono raccomandazioni o best practice in linea con gli standard REST/HTTP per includere tali metadati richiesti sulle richieste DELETE? Ci sono altre alternative che non ho considerato?

+1

Alcune implementazioni come 'Jersey' non consentono il corpo per richieste' delete'. – basiljames

+0

Sembra davvero che non ci sia una buona risposta a questo problema. – niico

risposta

32

Nonostante alcune raccomandazioni di non utilizzare il corpo del messaggio per le richieste DELETE, questo approccio potrebbe 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.

Mentre l'uso del corpo del messaggio non è l'ideale, nessuna delle altre opzioni si adattava perfettamente. Il corpo della richiesta DELETE ci ha permesso di aggiungere facilmente e con chiarezza semantica 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!

+7

Questa è una cattiva idea. Un posto dove questo ti metterà nei guai è se in seguito deciderai di utilizzare un servizio di accelerazione HTTP come Akamai EdgeConnect. So per certo che EdgeConnect rimuove i corpi dalle richieste DELETE HTTP (dal momento che consumano la larghezza di banda sono probabilmente non valide). È 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/classic-REST e i problemi di trasporto dei verbi HTTP sono molto difficili da risolvere. – Gabe

+1

Concordo con @Gabe, inviando un corpo con metodi che non hanno corpo ** per definizione ** è un modo infallibile per perdere dati casualmente mentre i tuoi bit attraversano le pipe di Internet, e avrai un debugging molto difficile esso. –

5

Data la situazione che hai, vorrei prendere uno dei seguenti approcci:

  • Invia un PUT o PATCH: sto deducendo che l'operazione di eliminazione è virtuale, per la natura del bisogno di una eliminazione ragionare. Pertanto, ritengo che l'aggiornamento del record tramite un'operazione PUT/PATCH sia un approccio valido, anche se non è un'operazione DELETE di per sé.
  • Utilizzare i parametri di query: L'uri della risorsa non viene modificato. In realtà penso che questo sia anche un approccio valido. La domanda che hai collegato stava parlando di non consentire l'eliminazione se il parametro della query mancava. Nel tuo caso, avrei solo un motivo predefinito se il motivo non è specificato nella stringa di query. La risorsa sarà comunque resource/:id. Puoi renderlo rilevabile con le intestazioni di collegamento sulla risorsa per ciascun motivo (con un tag rel su ciascuna per identificare il motivo).
  • Utilizzare un endpoint separato per motivo: Utilizzo di un url come resource/:id/canceled. Questo in realtà cambia l'URI della richiesta e non è sicuramente RESTful. Ancora una volta, le intestazioni dei collegamenti possono renderlo rilevabile.

Ricordare che REST non è legge o dogma. Pensalo più come guida. Quindi, quando ha senso non seguire la guida per il dominio del problema, non farlo. Assicurati solo che i tuoi utenti API siano informati della varianza.

+0

Per quanto riguarda l'uso dei parametri di query, la mia comprensione è che la query fa parte dell'Unità di richiesta per [sezione 3.2] (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.2), e quindi utilizzando questo approccio (o allo stesso modo, gli endpoint separati) va contro la definizione del metodo [DELETE] (http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html), tale che " risorsa identificata dall'URI della richiesta "è stata cancellata. – shelley

+0

La risorsa è identificata dal percorso uri. Quindi, OTTENERE a '/ orders /: id' restituirebbe 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 supportato). Allo stesso modo, se invii DELETE a '/ orders /: id' o'/orders /: id? Reason = cancellato' o '/ orders /: id? Reason = bad_credit', stai ancora agendo sulla stessa risorsa sottostante. Per mantenere un'interfaccia uniforme, avrei un motivo predefinito in modo che l'invio della query param non sia richiesto. – codeprogression

+0

@shelley Hai ragione riguardo alle stringhe di query. La stringa di query è parte dell'URI. L'invio di una richiesta DELETE a '/ foo? 123' significa che si sta eliminando una risorsa diversa da quella che si avrebbe se si inviava DELETE a'/foo? 456'. –

0

Suggerisco di includere i metadati richiesti come parte della stessa gerarchia URI. Un esempio (Naive):

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

ad es.

DELETE /entries/range/01012012/31122012 - Eliminare tutte le voci comprese tra 01 gennaio 2012 al 31 dicembre 2012

Spero che questo aiuti.

+4

Non copre casi come l'invio di un motivo per la cancellazione, ad esempio campo di lavoro. – Kugel

+2

Wow. questa è una pessima idea. Se disponi di una quantità eccessiva di metadati, aumenterà le restrizioni sulle dimensioni dell'URI. –

+0

Questo approccio non segue le pratiche RESTful e non è raccomandato in quanto si avrà una struttura URI contorta. Gli endpoint vengono confusi con l'identificazione intrecciata delle risorse con i metadati e nel tempo diventeranno un incubo di manutenzione man mano che la tua API cambia. È molto più preferibile avere l'intervallo specificato nei parametri o nel carico utile della query che è la sostanza di questa domanda: capire l'approccio delle migliori pratiche al problema che direi non è questo. – digitaldreamer

8

Cosa ti sembra di volere è una delle due cose, nessuna delle quali sono una pura DELETE:

  1. Hai due operazioni, un PUT della ragione di eliminazione seguita da una DELETE della risorsa. Una volta cancellati, i contenuti della risorsa non sono più accessibili a nessuno. La 'ragione' non può contenere un collegamento ipertestuale alla risorsa eliminata. Oppure,
  2. Si sta tentando di modificare una risorsa da state=active a state=deleted utilizzando il metodo DELETE.Le risorse con stato = eliminato vengono ignorate dalla tua API principale ma potrebbero comunque essere leggibili da un amministratore o da qualcuno con accesso al database. Questo è permesso - DELETE non deve cancellare i dati di supporto per una risorsa, solo per rimuovere la risorsa esposta a quell'URI.

Qualsiasi operazione che richiede un corpo del messaggio su una richiesta DELETE può essere suddiviso in esso è la cosa più generale, un POST per fare tutti i compiti necessari con il corpo del messaggio, e un DELETE. Non vedo alcun motivo per rompere la semantica di HTTP.

+2

Cosa succede se la ragione 'PUT' riesce e la risorsa' DELETE' fallisce? Come si può prevenire lo stato incoerente? – Lightman

+0

@Lightman the PUT specifica solo l'intento. Può esistere senza un DELETE corrispondente, il che indicherebbe che qualcuno volesse cancellare ma o ha fallito o ha cambiato idea. L'inversione dell'ordine delle chiamate consentirebbe inoltre a DELETE di verificarsi senza un motivo: la fornitura di un motivo sarebbe quindi considerata semplicemente come annotazione. È per entrambi questi motivi che consiglierei di utilizzare l'opzione 2 di cui sopra, cioè di modificare lo stato del record sottostante in modo tale da far scomparire la risorsa HTTP dal suo attuale URL. Un garbage collector/admin può quindi eliminare i record –