HTTP PUT vs PATCH HTTP dans une API REST

Aperçu

Dans cet article rapide, nous examinons les différences entre les verbes HTTP PUT et PATCH et la sémantique des deux opérations.

Nous utiliserons Spring pour implémenter deux points de terminaison REST prenant en charge ces deux types d’opérations, et pour mieux comprendre les différences et la bonne façon de les utiliser.

Quand utiliser Put et Quand Patch?

Commençons par une déclaration simple et légèrement simple.

Lorsqu’un client doit remplacer entièrement une ressource existante, il peut utiliser PUT. Lorsqu’ils effectuent une mise à jour partielle, ils peuvent utiliser un CORRECTIF HTTP.

Par exemple, lors de la mise à jour d’un seul champ de la Ressource, l’envoi de la représentation complète de la Ressource peut être fastidieux et utilise beaucoup de bande passante inutile. Dans de tels cas, la sémantique de PATCH a beaucoup plus de sens.

Un autre aspect important à considérer ici est l’idempotence; PUT est idempotent; PATCH peut l’être, mais n’est pas nécessaire. Et donc, en fonction de la sémantique de l’opération que nous mettons en œuvre, nous pouvons également choisir l’une ou l’autre en fonction de cette caractéristique.

Implémentation de la logique PUT et PATCH

Disons que nous voulons implémenter l’API REST pour mettre à jour une source lourde avec plusieurs champs:

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

Tout d’abord, nous devons créer le point de terminaison qui gère une mise à jour complète de la ressource à l’aide de PUT:

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

Il s’agit d’un point de terminaison standard pour la mise à jour des ressources.

Maintenant, disons que le champ d’adresse sera souvent mis à jour par le client. Dans ce cas, nous ne voulons pas envoyer l’objet HeavyResource entier avec tous les champs, mais nous voulons pouvoir mettre à jour uniquement le champ d’adresse – via la méthode PATCH.

Nous pouvons créer un DTO HeavyResourceAddressOnly pour représenter une mise à jour partielle du champ d’adresse:

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

Ensuite, nous pouvons tirer parti de la méthode PATCH pour envoyer une mise à jour partielle:

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

Avec ce DTO plus granulaire, nous pouvons envoyer le champ que nous devons mettre à jour uniquement – sans la surcharge d’envoyer toute la source lourde.

Si nous avons un grand nombre de ces opérations de mise à jour partielle, nous pouvons également ignorer la création d’un DTO personnalisé pour chaque sortie – et utiliser uniquement une carte:

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

Cette solution nous donnera plus de flexibilité dans la mise en œuvre de l’API; cependant, nous perdons également certaines choses, telles que la validation.

Test PUT et PATCH

Enfin, écrivons des tests pour les deux méthodes HTTP. Tout d’abord, nous voulons tester la mise à jour de la ressource complète via la méthode 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());

L’exécution d’une mise à jour partielle est réalisée à l’aide de la méthode PATCH:

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

Nous pouvons également écrire un test pour une approche plus générique:

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

Traitement des Requêtes partielles Avec des Valeurs Null

Lorsque nous écrivons une implémentation pour une méthode de PATCH, nous devons spécifier un contrat indiquant comment traiter les cas où nous obtenons null comme valeur pour le champ d’adresse dans HeavyResourceAddressOnly.

Supposons que le client envoie la requête suivante:

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

Ensuite, nous pouvons gérer cela en définissant une valeur du champ d’adresse sur null ou simplement en ignorant une telle demande en la traitant comme sans changement.

Nous devrions choisir une stratégie pour gérer null et nous y tenir dans chaque implémentation de méthode de PATCH.

Conclusion

Dans ce tutoriel rapide, nous nous sommes concentrés sur la compréhension des différences entre les méthodes HTTP PATCH et PUT.

Nous avons implémenté un contrôleur de REPOS à ressort simple pour mettre à jour une Ressource via la méthode PUT et une mise à jour partielle à l’aide de PATCH.

L’implémentation de tous ces exemples et extraits de code se trouve dans le projet GitHub – il s’agit d’un projet Maven, il devrait donc être facile à importer et à exécuter tel quel.

Commencez avec Spring 5 et Spring Boot 2, grâce au cours Learn Spring :

>> DÉCOUVREZ LE COURS

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.