Devo usare PATCH o PUT nella mia API REST?


274

Voglio progettare il mio endpoint di riposo con il metodo appropriato per il seguente scenario.

C'è un gruppo Ogni gruppo ha uno stato. Il gruppo può essere attivato o inattivato dall'amministratore.

Dovrei progettare il mio punto finale come

PUT /groups/api/v1/groups/{group id}/status/activate

O

PATCH /groups/api/v1/groups/{group id}

with request body like 
{action:activate|deactivate}

1
Entrambi vanno bene. Ma dai un'occhiata alla RFC per il formato JSON PATCH ( tools.ietf.org/html/rfc6902 ). PATCH prevede di ottenere una sorta di documento diff / patch per il payload (e JSON non è uno di questi).
Jørn Wildt,

1
@ JørnWildt no, PUT sarebbe una scelta orribile. Cosa ci metti lì? PATCH è l'unica opzione sensata. Bene, in questo caso potresti usare il formato PATCH presentato nella domanda e semplicemente usare il metodo PUT; l'esempio PUT è semplicemente sbagliato.
thecoshman,

3
Non c'è nulla di sbagliato nell'esporre una o più proprietà come risorse autonome che un client può OTTENERE e modificare con PUT. Sì, l'URL dovrebbe quindi essere / groups / api / v1 / groups / {group id} / status al quale è possibile METTERE "attivo" o "inattivo" o OTTENERE per leggere lo stato corrente.
Jørn Wildt,

3
Ecco una buona spiegazione del modo in cui PATCH dovrebbe davvero essere usato: williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot
rishat

4
" activate" non è un'adeguata costruzione RESTful. Probabilmente stai tentando di aggiornare status"su attivo" o "non attivo". nel qual caso puoi PATCH a .../statuscon la stringa "attiva" o "non attiva" nel corpo. O se stai cercando di aggiornare un booleano su status.active, puoi PATCH .../status/activecon il booleano nel corpo
Augie Gardner,

Risposte:


328

Il PATCHmetodo è la scelta corretta qui mentre stai aggiornando una risorsa esistente - l'ID del gruppo. PUTdovrebbe essere usato solo se stai sostituendo una risorsa nella sua interezza.

Ulteriori informazioni sulla modifica parziale delle risorse sono disponibili in RFC 5789 . In particolare, il PUTmetodo è descritto come segue:

Diverse applicazioni che estendono il protocollo HTTP (Hypertext Transfer Protocol) richiedono una funzione per apportare modifiche parziali alle risorse. Il metodo HTTP PUT esistente consente solo una sostituzione completa di un documento. Questa proposta aggiunge un nuovo metodo HTTP, PATCH, per modificare una risorsa HTTP esistente.


1
Per essere onesti, puoi mettere la stringa "attivare" o "disattivare" nella risorsa. Dal momento che (sembra) esserci solo una cosa da attivare, sostituirla completamente non è un grosso problema. E consente una richiesta (insignificante) più piccola.
thoshoshman,

35
È importante notare che RFC 5789 è ancora in fase di proposta e non è stato ufficialmente accettato ed è attualmente contrassegnato come "irrata esiste". Questa "best practice" è molto dibattuta e tecnicamente PATCH non fa ancora parte dello standard HTTP.
fishpen0

4
Solo i miei 2 centesimi qualche anno dopo: potresti considerare lo stato stesso come una risorsa e, in tal caso, l'uso di PUT contro / status significherebbe tecnicamente sostituire la risorsa di stato a quel punto finale.
Jono Stewart,

3
Oserei discutere contro i documenti, anche se è "la" RFC. I documenti affermano che dovresti usare PATCH per modificare solo una parte di una risorsa, ma ha omesso l'importante che il metodo PATCH sia definito come metodo non idempotente. Perché? Se il metodo PUT è stato creato tenendo presente l'aggiornamento / la sostituzione dell'intera risorsa, allora perché il metodo PATCH non è stato creato come metodo idempotente come PUT, se il suo scopo era semplicemente aggiornare la parte di una risorsa? Per me, sembra più una differenza nell'idempotenza dell'aggiornamento, come "a = 5" (PUT) e "a = a + 5" (PATCH). Entrambi possono aggiornare l'intera risorsa.
Mladen B.

179

La R in REST sta per risorsa

(Il che non è vero, perché rappresenta la rappresentazione, ma è un buon trucco per ricordare l'importanza delle risorse in REST).

A proposito PUT /groups/api/v1/groups/{group id}/status/activate: si sta non aggiornando una "Attiva". Un "attivare" non è una cosa, è un verbo. I verbi non sono mai buone risorse. Una regola empirica: se l'azione, un verbo, è nell'URL, probabilmente non è RESTful .

Cosa stai facendo invece? O stai "aggiungendo", "rimuovendo" o "aggiornando" un'attivazione su un gruppo, o se preferisci: manipolare una risorsa "status" su un gruppo. Personalmente, userei le "attivazioni" perché sono meno ambigue del concetto "status": la creazione di uno stato è ambigua, la creazione di un'attivazione non lo è.

  • POST /groups/{group id}/activation Crea (o richiede la creazione di) un'attivazione.
  • PATCH /groups/{group id}/activationAggiorna alcuni dettagli di un'attivazione esistente. Poiché un gruppo ha una sola attivazione, sappiamo a quale risorsa di attivazione ci stiamo riferendo.
  • PUT /groups/{group id}/activationInserisce o sostituisce la vecchia attivazione. Poiché un gruppo ha una sola attivazione, sappiamo a quale risorsa di attivazione ci stiamo riferendo.
  • DELETE /groups/{group id}/activation Annullerà o rimuoverà l'attivazione.

Questo modello è utile quando l '"attivazione" di un gruppo ha effetti collaterali, come pagamenti effettuati, posta inviata e così via. Solo POST e PATCH possono avere tali effetti collaterali. Quando, ad esempio, una cancellazione di un'attivazione deve, per esempio, informare gli utenti via mail, DELETE non è la scelta giusta; in questo caso probabilmente si desidera creare una risorsa di disattivazione : POST /groups/{group_id}/deactivation.

È una buona idea seguire queste linee guida, perché questo contratto standard lo rende molto chiaro per i tuoi clienti e tutti i proxy e i livelli tra il cliente e te, sapere quando è sicuro riprovare e quando no. Diciamo che il client è da qualche parte con il wifi instabile e il suo utente fa clic su "Disattiva", che attiva un DELETE: Se ciò fallisce, il client può semplicemente riprovare, fino a quando non ottiene un 404, 200 o qualsiasi altra cosa che possa gestire. Ma se innesca un POST to deactivationnon sa riprovare: il POST lo implica.
Ogni client ora ha un contratto, che, se seguito, proteggerà dall'invio di 42 e-mail "il tuo gruppo è stato disattivato", semplicemente perché la sua libreria HTTP ha continuato a riprovare la chiamata al back-end.

Aggiornamento di un singolo attributo: usa PATCH

PATCH /groups/{group id}

Nel caso in cui desideri aggiornare un attributo. Ad esempio, lo "stato" potrebbe essere un attributo sui gruppi che può essere impostato. Un attributo come "status" è spesso un buon candidato per limitare a una whitelist di valori. Gli esempi usano alcuni schemi JSON non definiti:

PATCH /groups/{group id} { "attributes": { "status": "active" } }
response: 200 OK

PATCH /groups/{group id} { "attributes": { "status": "deleted" } }
response: 406 Not Acceptable

Sostituendo la risorsa, senza effetti collaterali usa PUT.

PUT /groups/{group id}

Nel caso in cui desideri sostituire un intero gruppo. Ciò non significa necessariamente che il server crei effettivamente un nuovo gruppo e ne elimini uno vecchio, ad esempio gli ID potrebbero rimanere gli stessi. Ma per i client, questo è ciò che PUT può significare: il client dovrebbe presumere che ottiene un elemento completamente nuovo, basato sulla risposta del server.

Il client dovrebbe, in caso di una PUTrichiesta, inviare sempre l'intera risorsa, avendo tutti i dati necessari per creare un nuovo elemento: normalmente gli stessi dati richiesti da una creazione POST.

PUT /groups/{group id} { "attributes": { "status": "active" } }
response: 406 Not Acceptable

PUT /groups/{group id} { "attributes": { "name": .... etc. "status": "active" } }
response: 201 Created or 200 OK, depending on whether we made a new one.

Un requisito molto importante è quello PUTidempotente: se si richiedono effetti collaterali durante l'aggiornamento di un gruppo (o la modifica di un'attivazione), è necessario utilizzare PATCH. Pertanto, quando l'aggiornamento comporta, ad esempio, l'invio di una e-mail, non utilizzare PUT.


3
Questo è stato molto istruttivo per me. "Questo schema è utile quando l '" attivazione "di un gruppo ha effetti collaterali" - Come mai questo schema è utile, in particolare per quanto riguarda quando le azioni hanno effetti collaterali, al contrario degli endpoint iniziali del PO
Abdul

1
@Abdul, il modello è utile per molte ragioni, ma per gli effetti collaterali, dovrebbe essere molto chiaro per un cliente, quali effetti ha un'azione. Quando, per esempio, un'app iOS decide di inviare l'intera rubrica come "contatti", dovrebbe essere estremamente chiaro quali effetti collaterali ha la creazione, l'aggiornamento, l'eliminazione, ecc. Di un contatto. Per evitare di inviare per e-mail di massa tutti i contatti, ad esempio.
Berkes,

1
In RESTfull PUT può anche modificare le entità Identity - Ad esempio l'ID PrimaryKey dove potrebbe causare il fallimento di una richiesta parallela. (ad esempio l'aggiornamento dell'intera entità deve eliminare alcune righe e aggiungerne di nuove, quindi creare nuove entità) Dove PATCH non deve mai essere in grado di farlo, consentendo un numero illimitato di richieste PATCH senza influire su altre "applicazioni"
Piotr Kula,

1
Risposta molto utile Grazie! Vorrei anche aggiungere un commento, proprio come nella risposta di Luke, sottolineando che la differenza tra PUT / PATCH non è solo l'aggiornamento completo / parziale, è anche l'idempotenza che è diversa. Questo non è stato un errore, è stata una decisione intenzionale e penso che non molte persone lo prendano in considerazione quando decidono l'utilizzo del metodo HTTP.
Mladen B.

1
I servizi di @richremer, come i modelli, sono astrazioni interne. Proprio come è una cattiva astrazione richiedere una relazione 1-1 tra modelli di endpoint REST e ORM o anche tabelle di database, è scarsa astrazione esporre i Servizi. L'esterno, l'API, deve comunicare modelli di dominio. Il modo in cui le implementate internamente non è un problema per l'API. Dovresti essere libero di passare da ActivationService a un flusso di attivazione basato su CQRS, senza dover modificare l'API.
Berkes,

12

Consiglio di utilizzare PATCH, poiché il "gruppo" di risorse ha molte proprietà, ma in questo caso si sta aggiornando solo il campo di attivazione (modifica parziale)

secondo la RFC5789 ( https://tools.ietf.org/html/rfc5789 )

Il metodo HTTP PUT esistente consente solo una sostituzione completa di un documento. Questa proposta aggiunge un nuovo metodo HTTP, PATCH, per modificare una risorsa HTTP esistente.

Inoltre, in maggiori dettagli,

La differenza tra le richieste PUT e PATCH si riflette nel modo in cui il server elabora l'entità inclusa per modificare la risorsa
identificata dall'URI di richiesta. In una richiesta PUT, l'entità inclusa viene considerata una versione modificata della risorsa memorizzata sul
server di origine e il client richiede la
sostituzione della versione archiviata . Con PATCH, tuttavia, l'entità racchiusa contiene una serie di istruzioni che descrivono come modificare una risorsa attualmente residente sul
server di origine per produrre una nuova versione. Il metodo PATCH influenza la risorsa identificata dall'URI di richiesta e può
anche avere effetti collaterali su altre risorse; cioè nuove risorse
può essere creato, o quelli esistenti modificati, mediante l'applicazione di un
PATCH.

PATCH non è né sicuro né idempotente come definito da [RFC2616], Sezione 9.1.

I clienti devono scegliere quando utilizzare PATCH anziché PUT. Ad
esempio, se la dimensione del documento patch è maggiore della dimensione dei
nuovi dati delle risorse che verrebbero utilizzati in un PUT, potrebbe
essere logico utilizzare PUT anziché PATCH. Un confronto con POST è ancora più difficile, perché POST viene utilizzato in modi molto diversi e può
comprendere operazioni simili a PUT e PATCH se il server lo sceglie. Se
l'operazione non modifica la risorsa identificata dall'URI di richiesta in modo prevedibile, è necessario considerare POST anziché PATCH
o PUT.

Il codice di risposta per PATCH è

Il codice di risposta 204 viene utilizzato perché la risposta non contiene un corpo del messaggio (che avrebbe una risposta con il codice 200). Si noti che potrebbero essere utilizzati anche altri codici di successo.

consultare anche http: //restcookbook.com/HTTP%20Methods/patch/

Avvertenza: un'API che implementa PATCH deve eseguire una patch atomica. NON DEVE essere possibile che le risorse siano applicate a metà patch quando richiesto da un GET.


7

Poiché vuoi progettare un'API usando lo stile architettonico REST, devi pensare ai tuoi casi d'uso per decidere quali concetti sono abbastanza importanti da esporre come risorse. Se decidessi di esporre lo stato di un gruppo come sotto-risorsa, potresti dargli il seguente URI e implementare il supporto per entrambi i metodi GET e PUT:

/groups/api/groups/{group id}/status

L'aspetto negativo di questo approccio rispetto a PATCH per la modifica è che non sarai in grado di apportare modifiche a più di una proprietà di un gruppo in modo atomico e transazionale. Se le modifiche transazionali sono importanti, utilizzare PATCH.

Se decidi di esporre lo stato come risorsa secondaria di un gruppo, dovrebbe essere un collegamento nella rappresentazione del gruppo. Ad esempio, se l'agente ottiene il gruppo 123 e accetta XML, il corpo della risposta potrebbe contenere:

<group id="123">
  <status>Active</status>
  <link rel="/linkrels/groups/status" uri="/groups/api/groups/123/status"/>
  ...
</group>

È necessario un collegamento ipertestuale per soddisfare l' hypermedia come motore della condizione dello stato dell'applicazione dello stile architettonico REST.


0

Preferirei in genere qualcosa di un po 'più semplice, come activate/ deactivatesub-risorsa (collegato da Linkun'intestazione con rel=service).

POST /groups/api/v1/groups/{group id}/activate

o

POST /groups/api/v1/groups/{group id}/deactivate

Per il consumatore, questa interfaccia è semplicissima e segue i principi REST senza impedirti di concettualizzare le "attivazioni" come risorse individuali.


0

Una possibile opzione per attuare tale comportamento è

PUT /groups/api/v1/groups/{group id}/status
{
    "Status":"Activated"
}

E ovviamente, se qualcuno deve disattivarlo, PUTavrà lo Deactivatedstato in JSON.

In caso di necessità di attivazione / disattivazione di massa, PATCHpuò entrare nel gioco (non per il gruppo esatto, ma per la groupsrisorsa:

PATCH /groups/api/v1/groups
{
    { “op”: “replace”, “path”: “/group1/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group7/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group9/status”, “value”: “Deactivated” }
}

In generale questa è l'idea come suggerisce @Andrew Dobrowolski, ma con lievi cambiamenti nella realizzazione esatta.

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.