JSON Patch en ASP.NET Core 5 Web API
Introducción
Los verbos más habituales a la hora de trabajar con una Web API suelen ser GET, PUT y POST.
Y en menor medida PATCH y DELETE.
PUT y PATCH se utilizan para actualizar recursos existentes, pero la diferencia entre PUT y PATCH, es que PUT actualiza, o mejor dicho reemplaza, un recurso existente, mientras que PATCH, especifica únicamente los cambios.
JSON Patch
De acuerdo a lo anterior, ya tenemos claro que PUT y PATCH no es exactamente lo mismo, pero… ¿y qué es JSON Patch?.
JSON Patch es un formato para especificar actualizaciones que serán aplicadas a un recurso.
La especificación RFC-6902 nos especifica al detalle JSON Patch.
En rasgos generales, un JSON Patch es un documento, con un array o matriz de operaciones.
Un ejemplo de JSON Patch sería:
[ {"op" : "replace", "path" : "/Name", "value" : "George"} ]
Se pueden anidar operaciones diferentes, pero cada operación identifica un cambio concreto.
Formato de un JSON Patch
De acuerdo al ejemplo de JSON Patch anterior, tenemos que tener en cuenta que un JSON Patch estará formado por tantas operaciones atómicas o individuales como consideremos, y que el formato de un JSON Patch está formado por las siguientes partes generales:
op
Para indicar el tipo de operación.
Las posibles operaciones son:
- add
- copy
- move
- remove
- replace
- test
path
Para indicar el elemento a actualizar.
Si nuestro objeto tiene niveles, dividiremos esos niveles con el carácter /
Imaginemos que tenemos una clase Person, que dentro de ésta tiene una clase Address, y esa clase una propiedad Street.
El path en este caso será algo similar a /address/street
value
Para indicar el nuevo valor del elemento a actualizar.
JSON Patch en ASP.NET Core 5
Y una vez llegados a este punto, vamos a mostrar cómo utilizar JSON Path en una Web API de ASP.NET Core 5 (aunque también lo podemos hacer en otras versiones de .NET Core).
He creado un proyecto de tipo Web Api.
Dentro de él, lo primero que debemos tener en cuenta es que necesitaremos un paquete NuGet.
Así que lo agregaremos al proyecto.
Una aclaración en este punto. Existe un paquete NuGet que deberíamos añadir y utilizar, y que se llama Microsoft.AspNetCore.JsonPatch, sin embargo, cabe destacar que en este ejemplo voy a utilizar Microsoft.AspNetCore.Mvc.NewtonsoftJson que hace referencia a Microsoft.AspNetCore.JsonPatch, motivo por el cuál, no necesito agregar dicho paquete NuGet al proyecto.
Una vez hecho esto, he creado una carpeta Models dentro de la cuál he creado una clase Person con el siguiente código:
public class Person { public Guid Id { get; set; } public string Name { get; set; } public int Age { get; set; } }
Por otro lado, he creado una carpeta Services con una interfaz de nombre IDataService y una clase de nombre DataService, cuyo código es el siguiente:
public interface IDataService { Guid Add(Person person); Person GetBy(Guid id); List<Person> GetPeople(); }
y
public class DataService : IDataService { private List<Person> _people = new List<Person>(); public Guid Add(Person person) { person.Id = Guid.NewGuid(); _people.Add(person); return person.Id; } public Person GetBy(Guid id) => _people.Where(x => x.Id == id).FirstOrDefault(); public List<Person> GetPeople() => _people; }
Una vez hecho esto, iremos a la clase Startup y nos situaremos en el método ConfigureSerrvices, dejándolo de esta forma:
public void ConfigureServices(IServiceCollection services) { services.AddControllers() .AddNewtonsoftJson(); services.AddSingleton<IDataService, DataService>(); }
Ahora que ya tenemos preparada la parte central de toda nuestra Web API, vamos a trabajar en los controladores.
En nuestro caso, he creado un controlador de nombre EmployeeController.
Dentro de este controlador he añadido cuatro operaciones. Las cuatro operaciones que tengo en el servicio.
Dos de esas operaciones son operaciones GET.
Otra de ellas es una operación POST para insertar datos.
Y la última es una operación PATCH, que es precisamente lo que trato de explicar en esta entrada.
El constructor del controlador, resuelve la dependencia de IDataService. Y las operaciones se ejecutan a través de este servicio.
El código completo de nuestro controlador queda de la siguiente forma:
[ApiController] [Route("api/[controller]")] public class EmployeeController : ControllerBase { private readonly IDataService _dataService; public EmployeeController(IDataService dataService) { _dataService = dataService; } [HttpGet] public IActionResult Get() => Ok(_dataService.GetPeople()); [HttpGet("{id}")] public IActionResult GetBy(Guid id) => Ok(_dataService.GetBy(id)); [HttpPatch("update/{id}")] public IActionResult Patch(Guid id, [FromBody] JsonPatchDocument<Person> personPatch) { if (personPatch != null) { var person = _dataService.GetBy(id); if (person != null) { personPatch.ApplyTo(person); return Ok(person); } } return BadRequest(); } [HttpPost] public IActionResult Add(Person person) => Ok(_dataService.Add(person)); }
Desde Postman u otro programa, podemos hacer peticiones a nuestra Web API para jugar con ella.
Al principio no tiene datos, por lo que realizando una llamada a la operación POST, deberíamos recibir información sobre la operación como por ejemplo:
Si ejecutamos las operaciones GET, tanto para obtener todos los empleados, como para obtener que el que acabamos de crear, obtendremos sus datos.
Por ejemplo, la operación GET para obtener el empleado recién creado, quedaría de esta forma:
Y por último, la operación PATCH en la que realizaremos dos modificaciones sobre el elemento creado, implica dos operaciones.
Una por cada modificación.
En nuestro caso, modificaremos el nombre y la edad del empleado, así que nuestro JSON tendrá un aspecto similar al siguiente:
[ {"op" : "replace", "path" : "/Name", "value" : "Frank"}, {"op" : "replace", "path" : "/Age", "value" : "37"} ]
Un array o matriz de operaciones.
Dentro de Postman, tendrá un aspecto similar al siguiente:
Y obviamente, si obtenemos todos nuestros empleados, veremos que allí aparece el empleado que habíamos creado en un primer momento, con las modificaciones realizadas previamente.
Por cierto, a modo aclaratorio o a tener en consideración. Al realizar una petición de tipo Patch, lo habitual será recibir como respuesta un 204 No Content en el caso de que el payload de la respuesta no devuelva nada, o un 200 Ok en el caso de que sí devolvamos información.
Conclusiones
Como podemos observar, el uso de PATCH además de ser muy flexible, es muy sencillo de acometer.
Basta con tener cierto orden o criterio a la hora de saber qué operaciones vamos a realizar, y de qué tipo.
Tener en consideración algún aspecto sobre la lógica de negocio en la que nos encontremos, y realizar las acciones correspondientes dentro del sistema, pero en general, es muy sencillo de acometer.
Podrás acceder al código completo del proyecto de demostración en mi repositorio de GitHub en este enlace.
Esta entrada forma parte del 3er calendario de Adviento de C#, que encontrarás en este enlace.
Happy Coding!