HTTP PUT versus HTTP PATCH in een REST API

1. Overzicht

In dit korte artikel kijken we naar verschillen tussen de HTTP PUT- en PATCH-werkwoorden en naar de semantiek van de twee bewerkingen.

We gebruiken Spring om twee REST-eindpunten te implementeren die deze twee soorten bewerkingen ondersteunen, en om de verschillen en de juiste manier om ze te gebruiken beter te begrijpen.

2. Wanneer Put en wanneer Patch gebruiken?

Laten we beginnen met een simpele en ietwat simpele verklaring.

Wanneer een klant een bestaande resource volledig moet vervangen, kan hij PUT gebruiken. Als ze een gedeeltelijke update uitvoeren, kunnen ze HTTP PATCH gebruiken.

Als u bijvoorbeeld een enkel veld van de bron bijwerkt, kan het verzenden van de volledige weergave van de bron omslachtig zijn en veel onnodige bandbreedte gebruiken. In dergelijke gevallen is de semantiek van PATCH veel logischer.

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

3. Implementeren van PUT- en PATCH-logica

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

openbare klasse HeavyResource {privé Integer-id; private String naam; privé String-adres; // ...

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

@PutMapping ("/ heavyresource / {id}") openbare ResponseEntity saveResource (@RequestBody HeavyResource heavyResource, @PathVariable ("id") String-id) {heavyResourceRepository.save (heavyResource, id); return ResponseEntity.ok ("bron opgeslagen"); }

Dit is een standaard eindpunt voor het bijwerken van bronnen.

Laten we nu zeggen dat het adresveld vaak door de klant wordt bijgewerkt. In dat geval, we willen niet het geheel verzenden HeavyResource object met alle velden, maar we willen de mogelijkheid om alleen het adres field - via de PATCH-methode.

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

openbare klasse HeavyResourceAddressOnly {privé Integer-id; privé String-adres; // ...}

Vervolgens kunnen we de PATCH-methode gebruiken om een ​​gedeeltelijke update te verzenden:

@PatchMapping ("/ heavyresource / {id}") openbare ResponseEntity partiëleUpdateName (@RequestBody HeavyResourceAddressOnly partiëleUpdate, @PathVariable ("id") String-id) {heavyResourceRepository.save (partiëleUpdate, id); return ResponseEntity.ok ("bronadres bijgewerkt"); }

Met deze meer gedetailleerde DTO kunnen we het veld verzenden dat we alleen hoeven te updaten - zonder de overhead van het verzenden van een geheel HeavyResource.

Als we een groot aantal van deze gedeeltelijke update-bewerkingen hebben, kunnen we ook het maken van een aangepaste DTO voor elke out overslaan - en alleen een kaart gebruiken:

@RequestMapping (value = "/ heavyresource / {id}", method = RequestMethod.PATCH, consumes = MediaType.APPLICATION_JSON_VALUE) openbare ResponseEntity PartialUpdateGeneric (@RequestBody Map-updates, @PathVariable ("id") String-id) {heavyResourceRepository.save updates, id); return ResponseEntity.ok ("bron bijgewerkt"); }

Deze oplossing geeft ons meer flexibiliteit bij het implementeren van API; we verliezen echter ook een paar dingen - zoals validatie.

4. Testen van PUT en PATCH

Laten we tot slot tests schrijven voor beide HTTP-methoden. Eerst willen we de update van de volledige bron testen via de PUT-methode:

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

Het uitvoeren van een gedeeltelijke update wordt bereikt door de PATCH-methode te gebruiken:

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

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

HashMap-updates = nieuwe HashMap (); updates.put ("adres", "5th avenue"); mockMvc.perform (patch ("/ heavyresource / 1") .contentType (MediaType.APPLICATION_JSON_VALUE) .content (objectMapper.writeValueAsString (updates))) .andExpect (status (). isOk ()); 

5. Gedeeltelijke verzoeken afhandelen met Nul Waarden

Wanneer we een implementatie voor een PATCH-methode schrijven, moeten we een contract specificeren over hoe de gevallen moeten worden behandeld wanneer we die krijgen nul als waarde voor de adres veld in het HeavyResourceAddressOnly.

Stel dat de klant het volgende verzoek verstuurt:

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

Dan kunnen we dit behandelen als het instellen van een waarde van de adres veld naar nul of gewoon een dergelijk verzoek negeren door het als geen verandering te beschouwen.

We zouden een strategie moeten kiezen voor het afhandelen nul en houd u eraan bij elke implementatie van de PATCH-methode.

6. 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 ​​bron bij te werken via de PUT-methode en een gedeeltelijke update met PATCH.

De implementatie van al deze voorbeelden en codefragmenten is te vinden in het GitHub-project - dit is een Maven-project, dus het moet gemakkelijk te importeren en uit te voeren zijn zoals het is.