HTTP PUT vs HTTP PATCH a REST API-ban

Áttekintés

Ebben a gyors cikkben a HTTP PUT és PATCH igék közötti különbségeket és a két művelet szemantikáját vizsgáljuk.

A Spring segítségével két olyan REST végpontot fogunk implementálni, amelyek támogatják ezt a kétféle műveletet, és jobban megértjük a különbségeket és a helyes használatukat.

Mikor használjuk a Put és mikor a Patch-et?

Kezdjük egy egyszerű, és kissé egyszerű megállapítással.

Ha egy ügyfélnek egy meglévő erőforrást kell teljesen lecserélnie, akkor a PUT-ot használhatja. Ha részleges frissítést végez, használhatja a HTTP PATCH-ot.

Az Resource egyetlen mezőjének frissítésekor például a teljes Resource-reprezentáció elküldése nehézkes lehet, és sok felesleges sávszélességet használ. Ilyen esetekben a PATCH szemantikájának sokkal több értelme van.

Egy másik fontos szempont, amit itt figyelembe kell venni, az idempotencia; a PUT idempotenciális; a PATCH lehet, de nem kötelező. És így – a megvalósítandó művelet szemantikájától függően – e tulajdonság alapján is választhatjuk az egyiket vagy a másikat.

PUT és PATCH logika megvalósítása

Tegyük fel, hogy egy több mezőt tartalmazó HeavyResource frissítéséhez szeretnénk megvalósítani a REST API-t:

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

Először is létre kell hoznunk azt a végpontot, amely az erőforrás teljes frissítését kezeli a PUT használatával:

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

Ez egy szabványos végpont az erőforrások frissítéséhez.

Tegyük fel, hogy a cím mezőt gyakran fogja frissíteni az ügyfél. Ebben az esetben nem akarjuk elküldeni a teljes HeavyResource objektumot az összes mezővel együtt, hanem csak a cím mező frissítésének lehetőségét szeretnénk – a PATCH metóduson keresztül.

Elkészíthetünk egy HeavyResourceAddressOnly DTO-t a cím mező részleges frissítésének reprezentálásához:

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

Következő lépésként kihasználhatjuk a PATCH metódust a részleges frissítés elküldéséhez:

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

Ezzel a részletesebb DTO-val csak a frissítendő mezőt tudjuk elküldeni – a teljes HeavyResource elküldésének terhei nélkül.

Ha sok ilyen részleges frissítési műveletünk van, akkor kihagyhatjuk az egyéni DTO létrehozását is minden egyes outhoz – és csak egy térképet használhatunk:

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

Ez a megoldás nagyobb rugalmasságot biztosít számunkra az API megvalósításában; azonban néhány dolgot el is veszítünk – például az érvényesítést.

PUT és PATCH tesztelése

Végül írjunk teszteket mindkét HTTP módszerhez. Először a teljes erőforrás PUT módszerrel történő frissítését akarjuk tesztelni:

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

A részleges frissítés végrehajtását a PATCH módszerrel érjük el:

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

Egy általánosabb megközelítésre is írhatunk tesztet:

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

Mikor a PATCH módszer implementációját írjuk, meg kell adnunk egy szerződést, hogy hogyan kezeljük azokat az eseteket, amikor a HeavyResourceAddressOnly címmező értékeként null értéket kapunk.

Tegyük fel, hogy az ügyfél a következő kérést küldi:

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

Azt úgy kezelhetjük, hogy a cím mező értékét nullára állítjuk, vagy egyszerűen figyelmen kívül hagyjuk az ilyen kérést, és úgy kezeljük, hogy nem változik.

Válasszunk egy stratégiát a null kezelésére, és ragaszkodjunk ehhez minden PATCH módszer implementációjában.

Következtetés

Ebben a gyors bemutatóban a HTTP PATCH és PUT módszerek közötti különbségek megértésére összpontosítottunk.

Egy egyszerű Spring REST vezérlőt implementáltunk egy Resource frissítésére a PUT metódussal és egy részleges frissítésre a PATCH metódussal.

Az összes példa és kódrészlet megvalósítása megtalálható a GitHub projektben – ez egy Maven projekt, így könnyen importálható és futtatható lesz, ahogy van.

Kezdje meg a Spring 5 és a Spring Boot 2 használatát a Tanulj Spring tanfolyamon keresztül :

>> KATTINTSON KI A TANFOLYAMOT

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.