HTTP PUT vs HTTP PATCH într-un REST API

Prezentare generală

în acest articol rapid, analizăm diferențele dintre verbele HTTP PUT și PATCH și semantica celor două operații.

vom folosi Spring pentru a implementa două puncte finale REST care susțin aceste două tipuri de operațiuni și pentru a înțelege mai bine diferențele și modul corect de utilizare a acestora.

când să utilizați Put și când Patch-uri?

să începem cu o declarație simplă și ușor simplă.

când un client trebuie să înlocuiască în întregime o resursă existentă, poate utiliza PUT. Când fac o actualizare parțială, pot folosi patch-ul HTTP.

De exemplu, atunci când actualizați un singur câmp al resursei, trimiterea reprezentării complete a resurselor ar putea fi greoaie și utilizează o mulțime de lățime de bandă inutilă. În astfel de cazuri, semantica PATCH-ului are mult mai mult sens.

un alt aspect important de luat în considerare aici este idempotența; PUT este idempotent; PATCH-ul poate fi, dar nu este necesar. Și, deci-în funcție de semantica operațiunii pe care o implementăm, putem alege și una sau alta pe baza acestei caracteristici.

implementarea logicii PUT și PATCH

să presupunem că dorim să implementăm API-ul REST pentru actualizarea unei surse grele cu mai multe câmpuri:

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

în primul rând, trebuie să creăm punctul final care gestionează o actualizare completă a resursei folosind PUT:

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

acesta este un punct final standard pentru actualizarea resurselor.

acum, să spunem că câmpul de adresă va fi adesea actualizat de client. În acest caz, nu dorim să trimitem întregul obiect HeavyResource cu toate câmpurile, dar dorim posibilitatea de a actualiza doar câmpul de adresă – prin metoda PATCH-urilor.

putem crea un DTO HeavyResourceAddressOnly pentru a reprezenta o actualizare parțială a câmpului de adresă:

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

apoi, putem folosi metoda PATCH-urilor pentru a trimite o actualizare parțială:

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

cu acest DTO mai granular, putem trimite câmpul pe care trebuie să – l actualizăm numai-fără cheltuielile generale de trimitere a întregii resurse grele.

dacă avem un număr mare de aceste operații de actualizare parțială, putem sări peste crearea unui DTO personalizat pentru fiecare ieșire – și putem folosi doar o hartă:

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

această soluție ne va oferi mai multă flexibilitate în implementarea API; cu toate acestea, pierdem și câteva lucruri – cum ar fi validarea.

testare PUT și PATCH

în cele din urmă, să scriem teste pentru ambele metode HTTP. În primul rând, vrem să testăm actualizarea resursei complete prin metoda PUT:

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

executarea unei actualizări parțiale se realizează prin utilizarea metodei PATCH:

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

De asemenea, putem scrie un test pentru o abordare mai generică:

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

manipularea cererilor parțiale cu valori nule

când scriem o implementare pentru o metodă de PATCH-uri, trebuie să specificăm un contract despre cum să tratăm cazurile când obținem null ca valoare pentru câmpul de adresă din HeavyResourceAddressOnly.

să presupunem că clientul trimite următoarea solicitare:

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

apoi ne putem ocupa de acest lucru ca setarea unei valori a câmpului de adresă la null sau doar ignorarea unei astfel de Cereri, tratând-o ca fără modificări.

ar trebui să alegem o strategie pentru manipularea null și să rămânem la ea în fiecare implementare a metodei de PATCH-uri.

concluzie

în acest tutorial rapid, ne-am concentrat pe înțelegerea diferențelor dintre patch-ul HTTP și metodele PUT.

am implementat un simplu controler de odihnă de primăvară pentru a actualiza o resursă prin metoda PUT și o actualizare parțială folosind PATCH-uri.

implementarea tuturor acestor exemple și fragmente de cod poate fi găsită în proiectul GitHub – acesta este un proiect Maven, deci ar trebui să fie ușor de importat și rulat așa cum este.

începeți cu Spring 5 și Spring Boot 2, prin cursul Learn Spring :

>> verificați cursul

Lasă un răspuns

Adresa ta de email nu va fi publicată.