HTTP PUT vs PARCHE HTTP en una API REST

Descripción general

En este artículo rápido, analizamos las diferencias entre los verbos HTTP PUT y PATCH y la semántica de las dos operaciones.

Usaremos Spring para implementar dos endpoints REST que admiten estos dos tipos de operaciones, y para comprender mejor las diferencias y la forma correcta de usarlas.

¿Cuándo usar Put y Cuándo usar el parche?

Comencemos con una declaración simple y ligeramente simple.

Cuando un cliente necesita reemplazar un recurso existente por completo, puede usar PUT. Cuando están haciendo una actualización parcial, pueden usar el PARCHE HTTP.

Por ejemplo, al actualizar un solo campo del Recurso, enviar la representación de recursos completa puede ser engorroso y utilizar mucho ancho de banda innecesario. En tales casos, la semántica del PARCHE tiene mucho más sentido.

Otro aspecto importante a considerar aquí es idempotencia; PUT es idempotente; PATCH puede serlo, pero no es necesario. Y, por lo tanto, dependiendo de la semántica de la operación que estamos implementando, también podemos elegir una u otra basada en esta característica.

Implementando lógica PUT y PATCH

Digamos que queremos implementar la API REST para actualizar un recurso pesado con múltiples campos:

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

En primer lugar, necesitamos crear el punto final que maneje una actualización completa del recurso usando PUT:

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

Este es un punto de conexión estándar para actualizar recursos.

Ahora, digamos que el campo de dirección a menudo será actualizado por el cliente. En ese caso, no queremos enviar todo el objeto HeavyResource con todos los campos, pero sí queremos la capacidad de actualizar solo el campo de dirección a través del método PATCH.

Podemos crear un DTO de HeavyResourceAddressOnly para representar una actualización parcial del campo de dirección:

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

A continuación, podemos aprovechar el método de PARCHE para enviar una actualización parcial:

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

Con este DTO más granular, podemos enviar solo el campo que necesitamos actualizar – sin la sobrecarga de enviar recursos pesados completos.

Si tenemos un gran número de estas operaciones de actualización parcial, también podemos omitir la creación de un DTO personalizado para cada salida – y solo usar un mapa:

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

Esta solución nos dará más flexibilidad en la implementación de API; sin embargo, también perdemos algunas cosas, como la validación.

Testing PUT y PATCH

Finalmente, escribamos pruebas para ambos métodos HTTP. En primer lugar, queremos probar la actualización del recurso completo a través del método 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());

La ejecución de una actualización parcial se logra mediante el método de PARCHE:

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

También podemos escribir una prueba para un enfoque más genérico:

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

Manejo de Solicitudes Parciales Con Valores Nulos

Cuando escribimos una implementación para un método de PARCHE, necesitamos especificar un contrato de cómo tratar los casos cuando obtenemos null como un valor para el campo de dirección en HeavyResourceAddressOnly.

Supongamos que el cliente envía la siguiente solicitud:

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

Luego podemos manejar esto como establecer un valor del campo de dirección en nulo o simplemente ignorar dicha solicitud tratándola como sin cambios.

Debemos elegir una estrategia para manejar null y apegarnos a ella en cada implementación de método de PARCHE.

Conclusión

En este tutorial rápido, nos centramos en comprender las diferencias entre el PARCHE HTTP y los métodos PUT.

Implementamos un controlador de reposo de resorte simple para actualizar un Recurso a través del método PUT y una actualización parcial mediante PARCHE.

La implementación de todos estos ejemplos y fragmentos de código se puede encontrar en el proyecto GitHub – este es un proyecto Maven, por lo que debería ser fácil de importar y ejecutar como está.

Comience con Spring 5 y Spring Boot 2, a través del curso Learn Spring :

>> ECHA UN VISTAZO AL CURSO

Deja una respuesta

Tu dirección de correo electrónico no será publicada.