HTTP PUT vs HTTP PATCH in un’API REST

Panoramica

In questo breve articolo, stiamo esaminando le differenze tra i verbi HTTP PUT e PATCH e la semantica delle due operazioni.

Useremo Spring per implementare due endpoint REST che supportano questi due tipi di operazioni e per comprendere meglio le differenze e il modo giusto per utilizzarle.

Quando usare Put e quando Patch?

Iniziamo con una dichiarazione semplice e leggermente semplice.

Quando un client deve sostituire completamente una risorsa esistente, può utilizzare PUT. Quando stanno facendo un aggiornamento parziale, possono usare la PATCH HTTP.

Ad esempio, quando si aggiorna un singolo campo della Risorsa, l’invio della rappresentazione completa della risorsa potrebbe essere ingombrante e utilizza molta larghezza di banda non necessaria. In questi casi, la semantica della PATCH ha molto più senso.

Un altro aspetto importante da considerare qui è idempotence; PUT è idempotent; PATCH può essere, ma non è necessario. E, quindi, a seconda della semantica dell’operazione che stiamo implementando, possiamo anche scegliere l’una o l’altra in base a questa caratteristica.

l’Implementazione di METTERE PATCH e Logica

supponiamo di voler implementare l’API REST per l’aggiornamento di una HeavyResource con più campi:

public class HeavyResource { private Integer id; private String name; private String address; // ...

Primo, abbiamo bisogno di creare l’endpoint che gestisce un aggiornamento completo delle risorse utilizzando MESSO:

@PutMapping("/heavyresource/{id}")public ResponseEntity<?> saveResource(@RequestBody HeavyResource heavyResource, @PathVariable("id") String id) { heavyResourceRepository.save(heavyResource, id); return ResponseEntity.ok("resource saved");}

Questo è un endpoint standard per l’aggiornamento delle risorse.

Ora, diciamo che il campo indirizzo verrà spesso aggiornato dal client. In tal caso, non vogliamo inviare l’intero oggetto HeavyResource con tutti i campi, ma vogliamo la possibilità di aggiornare solo il campo dell’indirizzo, tramite il metodo PATCH.

Possiamo creare un DTO HeavyResourceAddressOnly per rappresentare un aggiornamento parziale del campo indirizzo:

public class HeavyResourceAddressOnly { private Integer id; private String address; // ...}

Successivamente, possiamo sfruttare il metodo PATCH per inviare un aggiornamento parziale:

@PatchMapping("/heavyresource/{id}")public ResponseEntity<?> partialUpdateName( @RequestBody HeavyResourceAddressOnly partialUpdate, @PathVariable("id") String id) { heavyResourceRepository.save(partialUpdate, id); return ResponseEntity.ok("resource address updated");}

Con questo DTO più granulare, possiamo inviare solo il campo che dobbiamo aggiornare, senza l’overhead di inviare l’intero HeavyResource.

Se abbiamo un gran numero di queste operazioni di aggiornamento parziale, possiamo anche saltare la creazione di un DTO personalizzato per ogni out-e utilizzare solo una mappa:

@RequestMapping(value = "/heavyresource/{id}", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE)public ResponseEntity<?> partialUpdateGeneric( @RequestBody Map<String, Object> updates, @PathVariable("id") String id) { heavyResourceRepository.save(updates, id); return ResponseEntity.ok("resource updated");}

Questa soluzione ci darà maggiore flessibilità nell’implementazione dell’API; tuttavia, perdiamo anche alcune cose, come la convalida.

Test PUT e PATCH

Infine, scriviamo test per entrambi i metodi HTTP. Innanzitutto, vogliamo testare l’aggiornamento della risorsa completa tramite il metodo PUT:

mockMvc.perform(put("/heavyresource/1") .contentType(MediaType.APPLICATION_JSON_VALUE) .content(objectMapper.writeValueAsString( new HeavyResource(1, "Tom", "Jackson", 12, "heaven street"))) ).andExpect(status().isOk());

L’esecuzione di un aggiornamento parziale si ottiene utilizzando il metodo PATCH:

mockMvc.perform(patch("/heavyrecource/1") .contentType(MediaType.APPLICATION_JSON_VALUE) .content(objectMapper.writeValueAsString( new HeavyResourceAddressOnly(1, "5th avenue"))) ).andExpect(status().isOk());

Possiamo anche scrivere un test per un approccio più generico:

HashMap<String, Object> updates = new HashMap<>();updates.put("address", "5th avenue");mockMvc.perform(patch("/heavyresource/1") .contentType(MediaType.APPLICATION_JSON_VALUE) .content(objectMapper.writeValueAsString(updates)) ).andExpect(status().isOk());

Gestione Parziale delle Richieste Con i Valori Null

Quando stiamo scrivendo un’implementazione per una PATCH metodo, dobbiamo specificare un contratto di come trattare i casi in cui si ottiene il valore null come valore per il campo indirizzo nella HeavyResourceAddressOnly.

Supponiamo che il client invii la seguente richiesta:

{ "id" : 1, "address" : null}

Quindi possiamo gestirlo come impostando un valore del campo indirizzo su null o semplicemente ignorando tale richiesta trattandola come no-change.

Dovremmo scegliere una strategia per gestire null e attenersi ad essa in ogni implementazione del metodo PATCH.

Conclusione

In questo breve tutorial, ci siamo concentrati sulla comprensione delle differenze tra la PATCH HTTP e i metodi PUT.

Abbiamo implementato un semplice controller Spring REST per aggiornare una risorsa tramite il metodo PUT e un aggiornamento parziale utilizzando PATCH.

L’implementazione di tutti questi esempi e frammenti di codice può essere trovata nel progetto GitHub – questo è un progetto Maven, quindi dovrebbe essere facile da importare ed eseguire così com’è.

Inizia con Spring 5 e Spring Boot 2, attraverso il corso Learn Spring :

>> CONTROLLA IL CORSO

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.