ASP.NET Core 2.2: Partial Updates with HTTP Patch (JSON Patch)

We use the HTTP PATCH method to apply partial updates to a resource. This means that, if we have a resource, and we want to update only a few fields of this, and not all, this is the HTTP method we should use.

How does HTTP PATCH work? The Web API client will send us, in the body of the HTTP request, information which will indicate the changes he wants to make in the resource. In our case, we are interested in knowing which fields the client wants to update and what the new values of these fields are going to be.

The question is, how is the client going to tell us this information? Well, surprisingly, RFC 5789 does not say much about how this structure should be. Luckily for us, there is a standard defined in RFC 6902 called “JSON Patch“, which we can use for the HTTP PATCH.

Basically, the JSON PATCH tells us what the structure of the body of the HTTP request should be so that it will indicate the changes that the client wants to make in the resource. This standard defines a set of operations, which are: add, remove, replace, move, copy and test. We are interested in replacing, which we will use to update fields of a resource.

An example of how the body of a set of replacements with JSON PATCH will look like:

[
  { "op": "replace", "path": "/name", "value": “Felipe Gavilan” },
  { "op": "replace", "path": "/birthdate", "value": “1905-01-02” }
]

Where “op” is the operation, in our case it is to replace. The “path” defines the field that we want to update, and, finally, the “value” defines the new value that the field will take. Notice that in the previous example we have to make two replacements, one to the name field and the other to the birthDate field.

Let’s then implement the JSON PATCH in our project. The most straightforward implementation is when we do not use a DTO, for example:

[HttpPatch("{id}")]
public async Task<ActionResult> Patch(int id, [FromBody] JsonPatchDocument<Author> patchDoc)
{
  if (patchDoc == null)
  {
    return BadRequest();
  }

  var authorFromDB = await _context.Authors.FirstOrDefaultAsync(x => x.Id == id);

  if (authorFromDB == null)
  {
     return NotFound();
  }

  patchDoc.ApplyTo(authorFromDB, ModelState);

  var isValid = TryValidateModel(authorFromDB);

  if (!isValid){
     return BadRequest(ModelState);
  }

  await _context.SaveChangesAsync();

  return NoContent();
}

We will explain the previous implementation:

  • We see that we use the HttpPatch attribute to define that this action will be executed when we make an HTTP PATCH on the corresponding endpoint
  • We use JsonPatchDocument as an input parameter of the function to define the possible values ​​of the HTTP PATCH operations, that is, for example, which fields we have available to edit.
  • Verify that patchDoc is not null, and, if so, return a 400.
  • We look for the corresponding record in the database, if the record does not exist, then we return a 404.
  • We use the ApplyTo function of the patchDoc variable. What this does is apply the operations on the authorFromDB object. That is, if we indicate that we want to update the property Name of the Author class, then only that property will be updated.
  • Then we try to validate the model to verify that all the validation rules have been respected, otherwise, we return a 400.
  • Finally, we save the changes and return a 204 No Content.

So, let’s try this. From Postman we can make an HTTP PATCH to the endpoint https:// localhost:5001/api/authors/1, with the following body:

[{
"op": "replace",
"path": "/name",
"value": "New name"
}]

The previous operation is a replacement that will occur over the Name field, and its new value will be “New name”. If we test the above, we will see that the operation has been successful, and that, effectively, the record with ID equal to 1 has been updated with the indicated value.

If we want to use DTOs, we can reuse the same DTO that we used for the HTTP PUT implementation:

[HttpPatch("{id}")]
public async Task<ActionResult> Patch(int id, [FromBody] JsonPatchDocument<AuthorCreationDTO> patchDoc)
{
  if (patchDoc == null)
  {
    return BadRequest();
  }

  var authorFromDB = await _context.Authors.FirstOrDefaultAsync(x => x.Id == id);

  if (authorFromDB == null)
  {
    return NotFound();
  }

  var authorDTO = _mapper.Map<AuthorCreationDTO>(authorFromDB);
  patchDoc.ApplyTo(authorDTO, ModelState);
  var isValid = TryValidateModel(authorFromDB);

  if (!isValid){
   return BadRequest(ModelState);
  }

 _mapper.Map(authorDTO, authorFromDB);

 await _context.SaveChangesAsync();

 return NoContent();
}

Here the extra steps that we do to be able to use DTOs are:

  1. to declare the JsonPatchDocument using the DTO, and,
  2. use AutoMapper in the corresponding places.

If we do the above, we see that our partial update continues to work, and, now we are not exposing our entity to our customers, which is the recommended practice when building WEB Apis.

Summary

  • HTTP PATCH is an HTTP method for performing partial updates
  • We can use JSON PATCH to implement partial updates in a simple way with Entity Framework Core

Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s