Json Patch (1) In ASP.NET Core web API

In the ASP.NET fields, both MVC and Web API modules, we are fimilar with creating the controller based on the output entity object to get CRUD actions for us, i.e., the Verbs: GET/POST/PUT/DELETE:
  • GET --- Select
  • POST --- Create
  • PUT --- Update
  • DELETE --- Delete
methods to access database.
 
However, there is one more Verb: Patch,available
 

The Methods and Verbs

 
Json Patch In ASP.NET Core web API
 
that is responsible to the patial update. We will discuss this issue in this article. We will discuss in the order below:
  • Introduction
    • Full Update vs. Patial Update
    • What is JSON Patch?
    • How Json Patch works?
  • Implementation in ASP.NET Core Web API, in Memory
    • Configure the App
    • Add PATCH related files (Entities and Controller)
    • Discussions:
      • Add Operation
      • Remove Operation
      • Replace Operation
      • Move Operation
      • Copy Operation
      • Test Operation
  • Implementation in ASP.NET Core Web API, for Database

A: Introduction

 

1, Full Update vs. Patial Update

 
The Verb PUT update the database the whole record.  However, sometimes, the client only want to update one field or partial fields in the records, sometimes, client side even does not have full info to update the records in whole, in this situation, a partial update is a better solution or required, that is what PATCH jumps in.
 
Therefore, the HTTP Verbs will be like this,
 
Json Patch In ASP.NET Core web API
 

2, What is Json Patch?

 
JSON Patch is a format for describing changes to a JSON document. It can be used to avoid sending a whole document when only a part has changed. When used in combination with the HTTP PATCH method, it allows partial updates for HTTP APIs in a standards compliant way.
 
JSON Patch is specified in RFC 6902 from the IETF.
 

3, How Json Patch works?

 
A JSON Patch document is just a JSON file containing an array of patch operations. The patch operations supported by JSON Patch are “add”, “remove”, “replace”, “move”, “copy” and “test”. The operations are applied in order: if any of them fail then the whole patch operation should abort.
 
Json Patch In ASP.NET Core web API
 

B: Implementation in ASP.NET Core Web API, in Memory

 
We will demo the PATCH result in ASP.NET Core Web API app with Swagger support.
 

1, Configure the App

 

Step 1: Create an ASP.NET Core MVC application

 
We use the current version of Visual Studio 2019 16.9.3 and .NET 5.0 SDK to Create an ASP.NET Core Web API app, see here for details.
 

Step 2: Configure app

 
To enable JSON Patch support, we need to install the Microsoft.AspNetCore.Mvc.NewtonsoftJson from NuGet package
 

2, Add PATCH related files:

 

Step 1: Add two entity classes:

 
Add two classes: Customer and Order:
  1. using System.Collections.Generic;  
  2.   
  3. namespace JsonPatchSample.Models  
  4. {  
  5.     public class Customer  
  6.     {  
  7.         public string CustomerName { getset; }  
  8.         public List<Order> Orders { getset; }  
  9.     }  

  1. namespace JsonPatchSample.Models  
  2. {  
  3.     public class Order  
  4.     {  
  5.         public string OrderName { getset; }  
  6.         public string OrderType { getset; }  
  7.     }  

Step 2: Add a Controller:

 
Add a tmpty controller named as: HomeController with following code:  
  1. using JsonPatchSample.Models;    
  2. using Microsoft.AspNetCore.JsonPatch;    
  3. using Microsoft.AspNetCore.Mvc;    
  4. using System.Collections.Generic;    
  5. using System.Dynamic;    
  6.     
  7. namespace JsonPatchSample.Controllers    
  8. {    
  9.     [Route("jsonpatch/[action]")]    
  10.     [ApiController]    
  11.     public class HomeController : ControllerBase    
  12.     {    
  13.         [HttpGet]    
  14.         public IActionResult JsonPatchWithModelState()   
  15.         {    
  16.             var customer = CreateCustomer();    
  17.             return new ObjectResult(customer);    
  18.         }    
  19.    
  20.         [HttpPatch]    
  21.         public IActionResult JsonPatchWithModelState(    
  22.             [FromBody] JsonPatchDocument<Customer> patchDoc)    
  23.         {    
  24.             if (patchDoc != null)    
  25.             {    
  26.                 var customer = CreateCustomer();    
  27.     
  28.                 patchDoc.ApplyTo(customer, ModelState);    
  29.     
  30.                 if (!ModelState.IsValid)    
  31.                 {    
  32.                     return BadRequest(ModelState);    
  33.                 }    
  34.     
  35.                 return new ObjectResult(customer);    
  36.             }    
  37.             else    
  38.             {    
  39.                 return BadRequest(ModelState);    
  40.             }    
  41.         }   
  42.   
  43.         private Customer CreateCustomer()  
  44.         {  
  45.             return new Customer  
  46.             {  
  47.                 CustomerName = "John",  
  48.                 Orders = new List<Order>()  
  49.                 {  
  50.                     new Order  
  51.                     {  
  52.                         OrderName = "Order0"  
  53.                     },  
  54.                     new Order  
  55.                     {  
  56.                         OrderName = "Order1"  
  57.                     }  
  58.                 }  
  59.             };  
  60.         }  
  61.     }    
  62. }  

3, Discussions:

 
Run the App:
 
For the .NET Core 5.0, there is a very good Swagger support for PATCH. If you run a previous version of .NET Core, it might not be the same. 
 
Json Patch In ASP.NET Core web API
 
Run the App, we have two action methods: GET and PATCH. Run the GET, we will get the Original input data:
 
Json Patch In ASP.NET Core web API
 
Run PATCH, we will run the PATCH operations: “add”, “remove”, “replace”, “move”, “copy” and “test”, one by one, all results are against to the original one above:
 

The add operation

  • If path points to an array element: inserts new element before the one specified by path.
  • If path points to a property: sets the property value.
  • If path points to a nonexistent location:
    • If the resource to patch is a dynamic object: adds a property.
    • If the resource to patch is a static object: the request fails.
The following sample patch document sets the value of CustomerName and adds an Order object to the end of the Orders array.
  1. [  
  2.   {  
  3.     "op""add",  
  4.     "path""/customerName",  
  5.     "value""Barry"  
  6.   },  
  7.   {  
  8.     "op""add",  
  9.     "path""/orders/-",  
  10.     "value": {  
  11.       "orderName""Order2",  
  12.       "orderType"null  
  13.     }  
  14.   }  
Shown in app like this,
 
Json Patch In ASP.NET Core web API
 
In the preceding JSON:
  • The op property indicates the type of operation.
  • The path property indicates the element to update.
  • The value property provides the new value.
Then we got the result:
 
Json Patch In ASP.NET Core web API
 
The changes made by applying a JSON Patch document to a resource are atomic. If any operation in the list fails, no operation in the list is applied.
 

The remove operation

  • If path points to an array element: removes the element.
  • If path points to a property:
    • If resource to patch is a dynamic object: removes the property.
    • If resource to patch is a static object:
      • If the property is nullable: sets it to null.
      • If the property is non-nullable, sets it to default<T>.
The following sample patch document sets CustomerName to null and deletes Orders[0]:
 
JSON
  1. [  
  2.   {  
  3.     "op""remove",  
  4.     "path""/customerName"  
  5.   },  
  6.   {  
  7.     "op""remove",  
  8.     "path""/orders/0"  
  9.   }  

 Then we got the result:
 
Json Patch In ASP.NET Core web API
 

The replace operation

 
This operation is functionally the same as a remove followed by an add.
 
The following sample patch document sets the value of CustomerName and replaces Orders[0]with a new Order object:
 
JSON
  1. [  
  2.   {  
  3.     "op""replace",  
  4.     "path""/customerName",  
  5.     "value""Barry"  
  6.   },  
  7.   {  
  8.     "op""replace",  
  9.     "path""/orders/0",  
  10.     "value": {  
  11.       "orderName""Order2",  
  12.       "orderType"null  
  13.     }  
  14.   }  

Then we got the result:
Json Patch In ASP.NET Core web API
 

The move operation

  • If path points to an array element: copies from element to location of path element, then runs a remove operation on the from element.
  • If path points to a property: copies value of from property to path property, then runs a remove operation on the from property.
  • If path points to a nonexistent property:
    • If the resource to patch is a static object: the request fails.
    • If the resource to patch is a dynamic object: copies from property to location indicated by path, then runs a remove operation on the from property.
The following sample patch document:
  • Copies the value of Orders[0].OrderName to CustomerName.
  • Sets Orders[0].OrderName to null.
  • Moves Orders[1] to before Orders[0].
JSON
  1. [  
  2.   {  
  3.     "op""move",  
  4.     "from""/orders/0/orderName",  
  5.     "path""/customerName"  
  6.   },  
  7.   {  
  8.     "op""move",  
  9.     "from""/orders/1",  
  10.     "path""/orders/0"  
  11.   }  

Then we got the result:
 
Json Patch In ASP.NET Core web API
 

The copy operation

 
This operation is functionally the same as a move operation without the final remove step.
 
The following sample patch document:
  • Copies the value of Orders[0].OrderName to CustomerName.
  • Inserts a copy of Orders[1] before Orders[0].
JSON
  1. [  
  2.   {  
  3.     "op""copy",  
  4.     "from""/orders/0/orderName",  
  5.     "path""/customerName"  
  6.   },  
  7.   {  
  8.     "op""copy",  
  9.     "from""/orders/1",  
  10.     "path""/orders/0"  
  11.   }  

 Then we got the result:
 
Json Patch In ASP.NET Core web API
 

The test operation

 
If the value at the location indicated by path is different from the value provided in value, the request fails. In that case, the whole PATCH request fails even if all other operations in the patch document would otherwise succeed.
 
The test operation is commonly used to prevent an update when there's a concurrency conflict.
 
The following sample patch document has no effect if the initial value of CustomerName is "John", because the test fails:
 
JSON
  1. [  
  2.   {  
  3.     "op""test",  
  4.     "path""/customerName",  
  5.     "value""Nancy"  
  6.   },  
  7.   {  
  8.     "op""add",  
  9.     "path""/customerName",  
  10.     "value""Barry"  
  11.   }  

 Then we got the result:
 
Json Patch In ASP.NET Core web API
 

C: Implementation in ASP.NET Core Web API, for Database

 
Now we run PATCH for View Models/DTOs from memory, and then we need to apply those patches back onto a database. Years ago, people use  Automapper to do the job, and now with .NET Core 5.0 (at least), we can do it just by the Microsoft code. We will demo a short sample here.
 
It seems even I just introduce the conclusion, this article wil bel still too long, I'd rather leave it to a new article as part II of this one.
 

Summary

 
Thia article described the PATCH concept briefly, and discussed the 6 PATCH operations associated with a ASP.NET Core Web API App.  We will discuss the PATCH Implementation in ASP.NET Core Web API, for Database, in the next article of part II of this article.
 
Reference