HTTP PUT vs HTTP PATCH i en REST API

oversigt

i denne hurtige artikel ser vi på forskelle mellem HTTP PUT og PATCH verber og på semantik af de to operationer.

vi bruger Spring til at implementere to REST-endepunkter, der understøtter disse to typer operationer, og til bedre at forstå forskellene og den rigtige måde at bruge dem på.

Hvornår skal du bruge Put og hvornår Patch?

lad os starte med en enkel og lidt enkel erklæring.

når en klient skal erstatte en eksisterende ressource helt, kan de bruge PUT. Når de laver en delvis opdatering, kan de bruge HTTP PATCH.

for eksempel, når du opdaterer et enkelt felt i ressourcen, kan det være besværligt at sende den komplette Ressourcerepræsentation og bruger en masse unødvendig båndbredde. I sådanne tilfælde giver LAPPENS semantik meget mere mening.

et andet vigtigt aspekt at overveje her er idempotens; PUT er idempotent; PATCH kan være, men er ikke påkrævet. Og så-afhængigt af semantikken i den operation, vi implementerer, kan vi også vælge den ene eller den anden baseret på denne egenskab.

implementering af PUT and PATCH Logic

lad os sige, at vi vil implementere REST API til opdatering af en HeavyResource med flere felter:

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

først skal vi oprette slutpunktet, der håndterer en fuld opdatering af ressourcen ved hjælp af PUT:

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

dette er et standard slutpunkt til opdatering af ressourcer.

lad os nu sige, at adressefeltet ofte opdateres af klienten. I så fald ønsker vi ikke at sende hele HeavyResource – objektet med alle felter, men vi ønsker muligheden for kun at opdatere adressefeltet-via PATCH-metoden.

vi kan oprette en HeavyResourceAddressOnly DTO for at repræsentere en delvis opdatering af adressefeltet:

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

dernæst kan vi udnytte PATCH-metoden til at sende en delvis opdatering:

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

med denne mere granulære DTO kan vi sende det felt, vi kun skal opdatere – uden at sende hele HeavyResource.

hvis vi har et stort antal af disse delvise opdateringsoperationer, kan vi også springe over oprettelsen af en brugerdefineret DTO for hver out-og kun bruge et kort:

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

denne løsning giver os mere fleksibilitet i implementeringen af API; vi mister dog også et par ting – såsom Validering.

test PUT and PATCH

endelig, lad os skrive test for begge HTTP-metoder. Først, Vi ønsker at teste opdateringen af den fulde ressource 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());

udførelse af en delvis opdatering opnås ved hjælp af PATCH-metoden:

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

vi kan også skrive en test for en mere generisk tilgang:

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

håndtering af delvise anmodninger med Null-værdier

når vi skriver en implementering til en PATCH-metode, skal vi specificere en kontrakt om, hvordan vi behandler sager, når vi får null som en værdi for adressefeltet i HeavyResourceAddressOnly.

Antag, at klienten sender følgende anmodning:

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

derefter kan vi håndtere dette som at indstille en værdi af adressefeltet til null eller bare ignorere en sådan anmodning ved at behandle den som ingen ændring.

vi bør vælge en strategi for håndtering null og holde sig til det i hver PATCH metode implementering.

konklusion

i denne hurtige tutorial fokuserede vi på at forstå forskellene mellem HTTP-PATCH og PUT-metoder.

Vi implementerede en simpel Spring REST controller til at opdatere en ressource via PUT metode og en delvis opdatering ved hjælp af PATCH.

implementeringen af alle disse eksempler og kodestykker findes i GitHub – projektet-Dette er et Maven-projekt, så det skal være let at importere og køre som det er.

kom i gang med Spring 5 og Spring Boot 2, gennem Learn Spring course :

>> tjek kurset

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.