HTTP PUT vs HTTP PATCH in een REST API

Overview

In dit snelle artikel kijken we naar verschillen tussen de HTTP PUT en PATCH werkwoorden en naar de semantiek van de twee operaties.

We zullen Spring gebruiken om twee REST endpoints te implementeren die deze twee soorten operaties ondersteunen, en om de verschillen en de juiste manier om ze te gebruiken beter te begrijpen.

Wanneer Put gebruiken en wanneer Patch?

Laten we beginnen met een eenvoudige, en enigszins simpele verklaring.

Wanneer een client een bestaande Resource volledig moet vervangen, kunnen ze PUT gebruiken. Wanneer ze een gedeeltelijke update doen, kunnen ze HTTP PATCH gebruiken.

Bij het updaten van een enkel veld van de Resource bijvoorbeeld, kan het verzenden van de complete Resource representatie omslachtig zijn en veel onnodige bandbreedte verbruiken. In dergelijke gevallen is de semantiek van PATCH veel zinvoller.

Een ander belangrijk aspect om hier te overwegen is idempotentie; PUT is idempotent; PATCH kan dat zijn, maar is dat niet verplicht. En, dus – afhankelijk van de semantiek van de operatie die we implementeren, kunnen we ook kiezen voor de een of de ander op basis van deze eigenschap.

Implementeren van PUT- en PATCH-logica

Laten we zeggen dat we de REST API willen implementeren voor het bijwerken van een HeavyResource met meerdere velden:

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

Eerst moeten we het eindpunt maken dat een volledige update van de resource afhandelt met behulp van PUT:

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

Dit is een standaard eindpunt voor het bijwerken van resources.

Nu, laten we zeggen dat het adresveld vaak zal worden bijgewerkt door de client. In dat geval willen we niet het hele HeavyResource-object met alle velden versturen, maar wel de mogelijkheid hebben om alleen het adresveld bij te werken – via de PATCH-methode.

We kunnen een HeavyResourceAddressOnly DTO maken om een gedeeltelijke update van het adresveld weer te geven:

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

Volgende, we kunnen de PATCH methode gebruiken om een gedeeltelijke update te verzenden:

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

Met deze meer granulaire DTO, kunnen we het veld verzenden dat we alleen moeten bijwerken – zonder de overhead van het verzenden van de hele HeavyResource.

Als we een groot aantal van deze gedeeltelijke update operaties, kunnen we ook overslaan de oprichting van een aangepaste DTO voor elke uit – en alleen gebruik maken van een map:

@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");}

Deze oplossing geeft ons meer flexibiliteit in de uitvoering van de API; maar we verliezen een paar dingen ook – zoals validatie.

Testen PUT en PATCH

Finishing, laten we schrijven tests voor beide HTTP-methoden. Ten eerste willen we de update van de volledige bron via de PUT methode testen:

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

Executie van een gedeeltelijke update wordt bereikt door gebruik te maken van de PATCH methode:

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

We kunnen ook een test schrijven voor een meer generieke aanpak:

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());

Handling Partial Requests With Null Values

Wanneer we een implementatie voor een PATCH-methode schrijven, moeten we een contract specificeren van hoe we gevallen moeten behandelen wanneer we null krijgen als waarde voor het adresveld in de HeavyResourceAddressOnly.

Stel dat de client het volgende verzoek verzendt:

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

Dan kunnen we dit afhandelen als het instellen van een waarde van het adresveld op null of gewoon negeren van een dergelijk verzoek door het te behandelen als geen-wijziging.

We moeten één strategie kiezen voor het afhandelen van null en ons daaraan houden in elke implementatie van de PATCH-methode.

Conclusie

In deze korte tutorial hebben we ons gericht op het begrijpen van de verschillen tussen de HTTP PATCH- en PUT-methoden.

We hebben een eenvoudige Spring REST controller geïmplementeerd om een Resource te updaten via de PUT methode en een gedeeltelijke update met behulp van PATCH.

De implementatie van al deze voorbeelden en code snippets kan worden gevonden in het GitHub project – dit is een Maven project, dus het zou eenvoudig te importeren en uit te voeren moeten zijn zoals het is.

Ga aan de slag met Spring 5 en Spring Boot 2, via de cursus Leer Spring :

>> CHECK OUT THE COURSE

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.