È corretto modificare parzialmente una raccolta con PUT o DELETE?


21

Ho una collezione di prodotti in un gruppo di prodotti, ad esempio:

product-groups/123/products
  1. Se devo aggiungere alla collezione, è OK che passo solo alcuni prodotti con PUT?

  2. Se devo eliminare alcuni prodotti dalla raccolta, è OK passare i dati del filtro (un array di ID) con DELETE?

Qual è il modo migliore per implementare la funzionalità nello spirito di ReST?

Modifica: gli articoli sono collegamenti a entità separate, sostanzialmente ID dei prodotti.


Gli articoli nel gruppo di prodotti sono separati da risorse gestite altrove? Oppure fanno solo parte della collezione del gruppo di prodotti? Se separati, i prodotti possono appartenere a più gruppi di prodotti?
Martijn Pieters,

2
forse PATCH Questa specifica definisce il nuovo metodo HTTP / 1.1 [RFC2616], PATCH, che viene utilizzato per applicare modifiche parziali a una risorsa.
Esailija,

Un prodotto (ID) può appartenere a diversi gruppi di prodotti.
user151851

Esiste un modo ben noto (best practice) per dire come PATCH, ovvero aggiungere o eliminare prodotti nella collezione?
user151851

Risposte:


10

In generale hai un endpoint che rappresenta l'intera raccolta di x :

/products

Dire, si desidera aggiornare un singolo prodotto, si effettua un PUT a /products/{id}. Se vuoi aggiornare parzialmente un singolo prodotto (non aggiornando tutti i campi), puoi anche usare un PATCH per /products/{id}. Lo stesso vale per l'eliminazione di una singola entità ( ELIMINA a /products/{id}).

Se vuoi scegliere come target una sola risorsa, ti qualifichi tramite percorso, quale risorsa singola , vuoi modificare.

L'unica azione che rompe lo schema è la creazione di una risorsa. Quando si crea una risorsa, si prende di mira la collezione nel suo insieme, dire POST a /products.

Detto questo, dovrebbe essere chiaro che l'obiettivo per le operazioni che incidono sulla raccolta nel suo insieme, dovrebbe andare all'endpoint di raccolta appropriato.

Ad esempio, vuoi recuperare un sottoinsieme di prodotti che sono rossi, lo chiedi per

ARRIVARE a /products?colour=red.

Quindi, se si desidera eliminare tutti questi, ELIMINA /products?colour=red . Oppure, se si desidera eliminare alcuni dei prodotti tramite id, è possibile eliminare /products?id=1&id=2&id=3 .

Che dire della creazione di massa di risorse? POST la tua collezione [{...},{...},{...}]semplicemente per /products. Lo stesso vale per PUT e PATCH .

È molto semplice.

Per rispondere alle tue domande:

Se devo aggiungere alla raccolta, è OK che passo solo alcuni prodotti con PUT?

Non è solo OK, sei incoraggiato a farlo in questo modo.

Se devo eliminare alcuni prodotti dalla raccolta, è OK passare i dati del filtro (un array di ID) con DELETE?

Va bene. Come ha scritto Eneko Alonso, a volte ci sono operazioni in blocco incapsulate tramite endpoint "controller", ovvero un POST viene utilizzato per innescare operazioni (complesse).


2
PUT è un'operazione di sostituzione. La chiamata di PUT su un endpoint di raccolta con "alcuni prodotti" dovrebbe eliminare (nel caso del PO, eliminare la relazione) qualsiasi prodotto che non sia incluso nell'elenco di "alcuni prodotti". Sebbene possa essere utilizzato per aggiungere elementi, dovrebbe anche rimuovere elementi che non sono (a mio avviso) previsti dal PO. Dovresti rivedere di conseguenza la tua risposta alla loro prima domanda.
Claytond,

@claytond: suppongo che la risposta sia corretta, purché venga effettuato un aggiornamento parziale PATCHe una sostituzione completa, tramite PUT.
9000

4
@ 9000. Certo, ma la risposta attualmente dice "sei incoraggiato a ... aggiungere alla collezione ... [by] pass [ing] solo alcuni prodotti con PUT". Questo è sicuramente errato. Incoraggiato a POST. In grado di mettere ... ma solo passando tutti (non alcuni) elementi.
Claytond,

5

Di solito, i metodi REST sono intesi per operare su una singola entità / oggetto (CRUD).

Esistono diverse opzioni:

  • Tratta le tue raccolte come entità e aggiornale tramite POST
  • Creare operazioni alternative, non REST

Il primo segue gli standard REST, ma può essere costoso, poiché gli oggetti / entità della raccolta potrebbero essere molto grandi (l'aggiornamento di un gruppo che ha migliaia di prodotti solo per aggiungere / rimuovere un prodotto sarebbe una richiesta pesante).

La seconda opzione è preferita da molte API, come modo per estendere il REST oltre le operazioni CRUD.

Per esempio:

GET product-groups/123/products (list all the products in the group)
POST product-groups/123/products/append (POST a list of new product ids to append to the group)
POST product-groups/123/products/remove (POST a list of product ids to remove from the group)

Molte API usano sempre POST per queste operazioni estese, ma nulla ti limita ad usare altri metodi http (oltre alla limitazione di GET e DELETE per avere un corpo vuoto)


Certo, ci sono alcuni metodi per raggiungere l'obiettivo. Qual è la migliore pratica? Quale sarà più a prova di futuro?
user151851

4
@ user151851: la conformità REST totale (se esiste una cosa del genere) è un obiettivo elevato. Il profilo dell'approccio qui sembra più realistico, in quanto tenta di impiegare un approccio che viene effettivamente utilizzato nel "mondo reale", rendendolo, in sostanza, un defacto-standard. Questo è a prova di futuro come avrai.
Robert Harvey,

2
Non introduciamo verbi personalizzati usando "append" ed "delete" nell'URL? In questo modo dovremo spiegare come utilizzare l'API. Non dovremmo riutilizzare ciò che abbiamo cioè i metodi HTTP? Nel qual caso le azioni sono ben note.
user151851

7
Per chiunque altro capiti in questa risposta: è sbagliato. Come accennato da @ user151851, questo introduce i verbi nell'URL che è meno RESTful possibile. Per quanto riguarda la vera domanda, non ho una grande risposta, ma questa non lo è.
umbrae

L '"estensione" potrebbe essere più orientata alle risorse rendendola products/collectionche restituisce una "busta" di articoli e il contenuto della busta viene modificato tramite un PUT? Ad esempio, "ecco esattamente come voglio che siano gli elementi nella raccolta".
Luke Puplett,

3

Solo per precise risposte / commenti precedenti.

Per quanto ne so, POST è il metodo per aggiungere singoli elementi alla raccolta.

ELIMINA a sua volta, è il metodo per eliminare un singolo elemento dalla raccolta. Entrambi gli scenari sono perfettamente RESTful.

Tuttavia, è necessario utilizzare l'URI appropriato per fare riferimento al singolo elemento o all'intera raccolta.

Ad esempio, per aggiungere un elemento alla raccolta è necessario POST dati al seguente URI:

https://www.factory.net/products/

Per eliminare un singolo prodotto dalla raccolta, è possibile utilizzare il metodo DELETE per inviare una richiesta a qualcosa del tipo:

https://www.factory.net/products/108/

Il metodo PATCH può essere utilizzato per aggiornare alcuni elementi all'interno della raccolta. Ad esempio, quando è necessario aggiornare solo un campo in un elemento. Mettere una rappresentazione completa delle risorse per una raccolta molto grande può essere un'operazione molto costosa.


2

In linea di principio, tutte le operazioni RESTful sono valide su una raccolta, ma assicurati di comprendere come la semantica dei verbi si applica a una raccolta:

  • PUT è un sostituto completo .

    • Se metti un singleton (es. /item/{id}) E lo lasci namefuori, dovrebbe essere cancellato o impostato su null o qualcosa di simile.
    • Se si inserisce una raccolta e non si include un elemento, è necessario rimuoverlo da quella raccolta.

    Mentre un PUT può essere utilizzato per aggiungere elementi, è necessario inviare "tutti" gli elementi. L'invio di "alcuni" elementi dovrebbe comportare la rimozione (suppongo che questo non sia ciò che l'OP desidera).

  • DELETE è più intuitivo. È valido per eliminare la raccolta o qualsiasi suo sottoinsieme filtrato. Solo gli elementi inclusi nel filtro dovrebbero essere interessati.

  • PATCH è anche valido. In teoria, dovresti fornire un elenco di "operazioni". Ad esempio, dovresti tecnicamente inviare qualcosa del tipo:

    [{ 
        "action": "update",
        "id": <id>,
        "value": {...}
    },{
        "action": "add",
        "value": {...}
    }, ...]
    

    In pratica, è più comune vedere un'API che accetta un elenco parziale di oggetti in cui ogni elemento viene elaborato utilizzando una logica UPSERT (aggiornamento o inserimento).

  • Tecnicamente, il POST dovrebbe elaborare l'input "in base alla semantica specifica della risorsa".

    • In pratica, il POST viene normalmente utilizzato per le operazioni di "creazione".
    • Tuttavia, POST è anche il verbo utilizzato per le chiamate non standard. Mentre c'è un acceso dibattito sul fatto che gli endpoint dell'azione siano rigorosamente RESTful (a parte i "no"), POST è il verbo appropriato se si inoltra una richiesta a un endpoint simile {resource}/activate.

NOTA: quando si utilizzano operazioni non GET sulle raccolte, considerare attentamente la definizione di successo e fallimento. REST non ti offre un buon modo per comunicare un successo parziale. Un buon valore predefinito è supporre che si eseguirà l'operazione in una transazione con un criterio di successo tutto o niente. Se questo non è ciò che desideri, probabilmente non dovresti interagire direttamente con la raccolta.

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.