Implementing JSON Merge Patch in ASP.NET Core WebAPI

Viewed 5592

I am interested in adding support for partial updates in my ASP.NET Core WebAPI where I only update the properties on a resource that the caller provided, leaving excluded properties unchanged.

For context, imagine I have a resource that can be described as follows:

GET /users/1
{
    title: "Mister",
    firstName: "Frederick",
    middleName: "McFeely",
    lastName: "Rodgers"
}

If I wanted to allow consumers to change the value stored in the firstName property from "Frederick" to "Fred" in isolation, I should be able to expose a PATCH endpoint that supports the JSON Merge Patch Content-Type, like so:

PATCH /users/1
Content-Type: application/merge-patch+json
{
    firstName: "Fred"
}

However, I see no easy way for me to know that firstName is the only property being updated. For example, if I were to make a controller that accepted PATCH verbs, it could be scaffolded like this:

[Route("users")]
public class UsersController : Controller {

    [HttpPatch("{userId:int}")]
    public User Patch([FromRoute] int userId, [FromBody] User user) {

        // How do I know which properties were set on User at this point?

    }

}

public class User {

    public String Title { get; set; }
    public String FirstName { get; set; }
    public String MiddleName { get; set; }
    public String LastName { get; set; }

}

But I don't see how I can extract which properties' had keys defined on the JSON object before it was hydrated as a User and passed to my controller. I cannot assume a value of null to mean a property was excluded as the caller could be explicitly setting an optional property to null.

Edit

I am aware of the Microsoft.AspNetCore.JsonPatch library. This, unfortunately, expects the caller to use the "[description of changes]" to define a PATCH as described in RFC 5789, which I find unintuitive and verbose. I am referring to the "JSON Merge Patch" defined in RFC 7396.

5 Answers

for simple types, I found a very simple solution using Newtonsoft.Json merge of JObjects:

public static T Patched<T>(T source, JObject patch) where T : class
{
    var sourceObject = JObject.FromObject(source);
    sourceObject.Merge(patch, new JsonMergeSettings() {MergeArrayHandling = MergeArrayHandling.Union});
    return sourceObject.ToObject<T>();
}

public static T Patched<T>(T source, string patchCode) where T : class
{
    return Patched<T>(source, JObject.Parse(patchCode));
}

Hope this helps someone searching for this topic and looking for a simple solution without external packages.

Related