HTTP PUT vs HTTP PATCH i en REST API

1. Oversigt

I denne hurtige artikel ser vi på forskelle mellem HTTP PUT- og PATCH-verbene og på semantikken i de to operationer.

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

2. Hvornår skal jeg bruge Put og Hvornår?

Lad os starte med en enkel og let simpel erklæring.

Når en klient skal udskifte en eksisterende ressource helt, kan de bruge PUT. Når de foretager 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 bruge meget unødvendig båndbredde. I sådanne tilfælde giver PATCHs semantik meget mere mening.

Et andet vigtigt aspekt at overveje her er ledighed PUT er idempotent; PATCH kan være, men det er ikke nødvendigt. 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.

3. Implementering af PUT- og PATCH-logik

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

offentlig klasse HeavyResource {privat heltal-id; privat strengnavn; privat strengadresse; // ...

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

@PutMapping ("/ heavyyresource / {id}") offentlig ResponseEntity saveResource (@RequestBody HeavyResource heavyResource, @PathVariable ("id") Streng-id) {heavyResourceRepository.save (heavyResource, id); returnere ResponseEntity.ok ("ressource gemt"); }

Dette er et standard slutpunkt til opdatering af ressourcer.

Lad os nu sige, at adressefeltet ofte opdateres af klienten. I det tilfælde, vi ønsker ikke at sende det hele HeavyResource objekt med alle felter, men vi ønsker evnen til kun at opdatere adresse felt - via PATCH-metoden.

Vi kan oprette en HeavyResourceAddressOnly DTO til at repræsentere en delvis opdatering af adressefeltet:

offentlig klasse HeavyResourceAddressOnly {private heltal id; privat strengadresse; // ...}

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

@PatchMapping ("/ heavyyresource / {id}") offentlig ResponseEntity partialUpdateName (@RequestBody HeavyResourceAddressOnly partialUpdate, @PathVariable ("id") Streng-id) {heavyResourceRepository.save (partialUpdate, id); returnere ResponseEntity.ok ("ressource adresse opdateret"); }

Med denne mere detaljerede DTO kan vi kun sende det felt, vi har brug for at opdatere - uden omkostningerne ved at sende hele HeavyResource.

Hvis vi har et stort antal af disse delvise opdateringsoperationer, kan vi også springe over oprettelsen af ​​en brugerdefineret DTO til hver ud - og kun bruge et kort:

@RequestMapping (værdi = "/ heavyyresource / {id}", metode = RequestMethod.PATCH, forbruger = MediaType.APPLICATION_JSON_VALUE) offentlig ResponseEntity partialUpdateGeneric (@RequestBody Map opdateringer, @PathVariable ("id") String id) {heavyResourceReport opdateringer, id); returnere ResponseEntity.ok ("ressource opdateret"); }

Denne løsning giver os større fleksibilitet i implementeringen af ​​API; dog mister vi også et par ting - såsom validering.

4. Test af PUT og PATCH

Lad os endelig skrive test til begge HTTP-metoder. Først vil vi teste opdateringen af ​​den fulde ressource via PUT-metoden:

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

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

mockMvc.perform (patch ("/ heavyyrecource / 1") .contentType (MediaType.APPLICATION_JSON_VALUE) .content (objectMapper.writeValueAsString (new HeavyResourceAddressOnly (1, "5th avenue")))). og Expect (status () isO). );

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

HashMap opdateringer = nye HashMap (); updates.put ("adresse", "5. avenue"); mockMvc.perform (patch ("/ heavyyresource / 1") .contentType (MediaType.APPLICATION_JSON_VALUE) .content (objectMapper.writeValueAsString (opdateringer))). og Expect (status (). isOk ()); 

5. Håndtering af delvise anmodninger med Nul Værdier

Når vi skriver en implementering af en PATCH-metode, skal vi specificere en kontrakt om, hvordan vi skal behandle sager, når vi får det nul som en værdi for adresse felt i HeavyResourceAddressOnly.

Antag, at klienten sender følgende anmodning:

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

Så kan vi håndtere dette som at indstille en værdi af adresse felt til nul eller bare ignorere en sådan anmodning ved at behandle den som ingen ændring.

Vi bør vælge en strategi til håndtering nul og hold dig til det i hver implementering af PATCH-metoden.

6. Konklusion

I denne hurtige vejledning fokuserede vi på at forstå forskellene mellem HTTP PATCH og PUT-metoderne.

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.