áttekintés
ebben a gyors cikkben a HTTP PUT és a PATCH igék közötti különbségeket, valamint a két művelet szemantikáját vizsgáljuk.
a Spring segítségével két REST végpontot valósítunk meg, amelyek támogatják ezt a két típusú műveletet, és jobban megértjük a különbségeket és a helyes használat módját.
mikor kell használni a Put és mikor tapaszt?
kezdjük egy egyszerű, kissé egyszerű kijelentéssel.
amikor egy ügyfélnek teljesen le kell cserélnie egy meglévő erőforrást, használhatja a PUT-ot. Amikor részleges frissítést végeznek, használhatják a HTTP javítást.
például az erőforrás egyetlen mezőjének frissítésekor a teljes erőforrás-ábrázolás elküldése nehézkes lehet, és sok felesleges sávszélességet igényel. Ilyen esetekben a PATCH szemantikája sokkal értelmesebb.
egy másik fontos szempont, amelyet itt figyelembe kell venni, az idempotencia; a PUT idempotens; a javítás lehet, de nem szükséges. Tehát-a végrehajtott 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 implementálása
tegyük fel, hogy a REST API-t szeretnénk implementálni egy HeavyResource frissítéséhez több mezővel:
public class HeavyResource { private Integer id; private String name; private String address; // ...
először 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ímmezőt gyakran frissíti az ügyfél. Ebben az esetben nem akarjuk elküldeni az egész HeavyResource objektumot az összes mezővel, de azt akarjuk, hogy csak a címmezőt frissítsük – a PATCH módszerrel.
létrehozhatunk egy Heavarresourceaddressonly DTO-t a cím mező részleges frissítéséhez:
public class HeavyResourceAddressOnly { private Integer id; private String address; // ...}
következő, kihasználhatjuk a PATCH módszert részleges frissítés küldésére:
@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 szemcsésebb DTO – val csak azt a mezőt küldhetjük el, amelyet frissítenünk kell-anélkül, hogy az egész HeavyResource küldésének költsége lenne.
ha nagyszámú ilyen részleges frissítési műveletünk van, akkor kihagyhatjuk az egyéni DTO létrehozását is minden egyes kimenethez – és csak 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 is elveszítünk – például az érvényesítést.
tesztelés és javítás
végül írjunk teszteket mindkét HTTP-módszerhez. Először a teljes erőforrás frissítését szeretnénk tesztelni PUT módszerrel:
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ása a PATCH módszerrel érhető el:
mockMvc.perform(patch("/heavyrecource/1") .contentType(MediaType.APPLICATION_JSON_VALUE) .content(objectMapper.writeValueAsString( new HeavyResourceAddressOnly(1, "5th avenue"))) ).andExpect(status().isOk());
írhatunk egy tesztet egy általánosabb megközelítéshez is:
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());
részleges kérések kezelése Null értékekkel
amikor egy PATCH metódus implementációját írjuk, meg kell adnunk egy szerződést arról, hogyan kezeljük azokat az eseteket, amikor null értéket kapunk a HeavyResourceAddressOnly címmezőjének értékeként.
tegyük fel, hogy az ügyfél a következő kérést küldi:
{ "id" : 1, "address" : null}
ezután kezelhetjük ezt úgy, hogy a címmező értékét nullára állítjuk, vagy csak figyelmen kívül hagyjuk az ilyen kérést úgy, hogy nem változtatunk.
ki kell választanunk egy stratégiát A null kezelésére, és ragaszkodnunk kell hozzá minden PATCH módszer végrehajtásakor.
következtetés
ebben a gyors oktatóanyagban a HTTP javítás és a 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 erőforrás PUT módszerrel történő frissítéséhez, valamint egy részleges frissítést PATCH segítségével.
ezeknek a példáknak és kódrészleteknek a megvalósítása megtalálható a GitHub projektben – ez egy Maven projekt, így könnyen importálható és futtatható.
kezdje el a Spring 5 és a Spring Boot 2 használatával a Learn Spring tanfolyamon keresztül:
>> nézze meg a tanfolyamot