Immutable Properties With JSON Patch in AspNet Core

Maxwell Weru
Nov 17, 2020 · 4 min read
JSON Patch
JSON Patch

There are times you want ot make updates to a resource on the server without replacing the whole of it. JSON Patch works a great deal in this, lighter and very powerful. To learn more on JSON Patch check out these links below:

  1. Official site at http://jsonpatch.com
  2. Introductory Article by Kevin Sookocheff
  3. Explainer by Waqat Mansor

ASP.NET Core supports JSON Patch natively using the HttpPatchAttribute usually decorate on a controller action with [HttpPath], model binding support for JsonPatchDocument and JsonPatchDocument<T>, and model validation support when applying changes via extension method with the signature document.ApplyTo(document, ModelState). More on this can be found in the official documentation.

On this post I will not focus on how JSON Patch works, why it is better or how it is supported in ASP.NET Core (for that, see above links). Instead, I will focus on how you can ensure that certain properties are not edited in a patch operation or ensure that only certain properties can be edited.

TLDR; you can jump straight to the code.

Breakdown for parts?

There are many parts in this sample. So let’s dig in. The sample uses Entity Framework Core which I assume anyone working with ASPNET Core understands.

When working with JSON Patch it is a good idea to have different models for every level of information and then employ inheritance between them. For example, let us consider that the server store information about vehicles in a garage. The unique identifier (which could be the VIN, the registration number, or a randomly generated string) of the vehicle should not be changed after creation but the creator is allowed to specify it during creation. We would model 3 classes: VehiclePatchModel, VehicleCreateModel and Vehicle. Employing inheritance, VehicleCreateModel would inherit from VehiclePatchModel and Vehicle may optionally inherit from VehicleCreateModel to avoid duplicate properties, ease documentation and maintenance.

These models will also be used to separate the accepted properties at different stages. In the example, the properties that are accepted for the JSON Patch operation are those in the VehiclePatchModel.

Applying JSON patch operations on a target object can fail without throwing an exception or returning an error. In ASP.NET Core, this is mitigated using an extension method that takes in the ModelState so that it can write the errors to it. This samples, makes use of this function internally by checking the properties before executing it. The logic is shown below:

Checking allowed properties can be difficult. Instead of using a list of properties that are immutable, we check the properties that can be changed. In JSON Patch, paths are specified starting with a forward slash / so we have to make sure that we remove it. Also for arrays, lists and collection, the last segment in the path specifies the index or any. For example: /0 to address index zero, /30 to address index 30, or /- to not target a particular index such as when adding a new item to a list. Removing this segements can be tricky and can cause errors.

Nested properties are have a similar problem with indexes above. Our sample, assumes that all nested properties a patch model are not immutable.

Finally, to make the solution easier to use, the implementation is done using an extension method on JsonPatchDocument<T>.

Now that we have the models setup and the extension method, we now can need to bring the two together. This is where an MVC controller is used.

The ListAsync and GetAsync methods are straightfoward. The CreateAsync method works with the VehicleCreateModel to avoid setting properties that are only system generated. This is a fairly common pattern.

The fun part is in the UpdateAsync method which takes an important JsonPatchDocument<VehiclePatchModel> . This type sets the type which contains the properties that we can update. Since we employed inhertiance, we can apply JsonPatchDocument<VehiclePatchModel> to an object of type Vehicle . Easy …

Finally, when the patch document is applied on the object, we must check if the ModelState still remains valid. If there were errors in apply the JSON Patch operations, they would be written as errors in the ModelState dictionary. Returning Problem(); will pick errors from the ModelState, convert to ValidationProblemDetails and then write to the response.

To try out the sample, make a HTTP Patch request with an try to change the ChasisNo property. See SampleRequest.json for the body.

The first operation attempts to change an immutable property causing validation to fail and receiving a 400 (Bad Request) response. See SampleResponse.json for the body.

Removing the first operation from the request would result in completion of the request. This samples assumes that you have data in the database context before attempting a patch. If not, you can replace the use of Entity Framework with a static list of items for test purposes.

Conclusion

Working with JSON Patch can be awesome and painful at the same time. Hopefully, I have helped you make it easier with some sort of rules on what can be edited.

There are still some missing pieces on here:

  1. If there are many projects such as a solution with micro-services, repeating this can be tedious so it may be better to create a library that moves the repeated code out of the application project.
  2. If there are many types that are to be edited, one may consider moving the code out of the controller to a Validation attribute, model binding extensions or just somewhere else.
  3. The sample here may not cover all scenarios for nested properties.

The Startup

Medium's largest active publication, followed by +754K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store