HTTP PUT vs HTTP PATCH i en REST API

översikt

i denna snabba artikel, vi tittar på skillnader mellan HTTP PUT och PATCH verb och semantiken för de två operationerna.

vi använder Spring för att implementera två REST endpoints som stöder dessa två typer av operationer, och för att bättre förstå skillnaderna och rätt sätt att använda dem.

När ska man använda Put och när Patch?

låt oss börja med ett enkelt och lite enkelt uttalande.

när en klient behöver ersätta en befintlig resurs helt kan de använda PUT. När de gör en partiell uppdatering kan de använda HTTP-PATCH.

när du till exempel uppdaterar ett enda fält i resursen kan det vara besvärligt att skicka hela Resursrepresentationen och utnyttjar mycket onödig bandbredd. I sådana fall är semantiken i PATCH mycket mer meningsfull.

en annan viktig aspekt att tänka på här är idempotence; PUT är idempotent; PATCH kan vara, men krävs inte. Och så – beroende på semantiken i den operation vi genomför, kan vi också välja det ena eller det andra baserat på denna egenskap.

implementera PUT and PATCH Logic

låt oss säga att vi vill implementera REST API för att uppdatera en HeavyResource med flera fält:

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

först måste vi skapa slutpunkten som hanterar en fullständig uppdatering av resursen med PUT:

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

detta är en standard slutpunkt för uppdatering av resurser.

låt oss nu säga att adressfältet ofta uppdateras av klienten. I så fall vill vi inte skicka hela HeavyResource – objektet med alla fält, men vi vill ha möjligheten att bara uppdatera adressfältet-via PATCHMETODEN.

vi kan skapa en HeavyResourceAddressOnly DTO för att representera en partiell uppdatering av adressfältet:

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

därefter kan vi utnyttja PATCHMETODEN för att skicka en partiell uppdatering:

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

med denna mer granulära DTO kan vi skicka det fält vi bara behöver uppdatera-utan att skicka hela HeavyResource.

om vi har ett stort antal av dessa partiella uppdateringsoperationer kan vi också hoppa över skapandet av en anpassad DTO för varje ut-och bara använda en karta:

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

denna lösning ger oss mer flexibilitet när det gäller att implementera API; men vi förlorar också några saker – till exempel validering.

testa PUT och PATCH

slutligen, låt oss skriva tester för båda HTTP-metoderna. Först vill vi testa uppdateringen av hela resursen via PUT-metoden:

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

utförande av en partiell uppdatering uppnås med hjälp av PATCHMETODEN:

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

vi kan också skriva ett test för ett mer generiskt tillvägagångssätt:

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

hantering av partiella förfrågningar med Null-värden

när vi skriver en implementering för en PATCHMETOD måste vi ange ett avtal om hur man behandlar fall när vi får null som ett värde för adressfältet i HeavyResourceAddressOnly.

Antag att klienten skickar följande begäran:

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

då kan vi hantera detta som att ange ett värde för adressfältet till null eller bara ignorera en sådan begäran genom att behandla den som ingen ändring.

vi bör välja en strategi för hantering av null och hålla fast vid det i varje PATCH method implementation.

slutsats

i denna snabba handledning fokuserade vi på att förstå skillnaderna mellan HTTP-patchen och PUT-metoderna.

vi implementerade en enkel Spring REST controller för att uppdatera en resurs via PUT-metoden och en partiell uppdatering med PATCH.

implementeringen av alla dessa exempel och kodavsnitt finns i GitHub – projektet-det här är ett Maven-projekt, så det ska vara enkelt att importera och köra som det är.

Kom igång med Spring 5 och Spring Boot 2, genom Learn Spring course :

>> kolla in kursen

Lämna ett svar

Din e-postadress kommer inte publiceras.