Partial Updates for Your RESTful API with JsonPatch in the new ASP .NET 5

As some of you may know, I’ve been working with Microsoft on integrating one of my OSS projects, Marvin.JsonPatch, into the next version of ASP .NET.  Last week at BUILD, a new RC build of Visual Studio 2015 was released.  This version includes the new ASP .NET, and is thus the first one that also includes support for JsonPatch out of the box.

A few words on what JsonPatch actually is:

JSON Patch (https://tools.ietf.org/html/rfc6902) defines a JSON document structure for expressing a sequence of operations to apply to a JavaScript Object Notation (JSON) document; it is suitable for use with the HTTP PATCH method.

One of the things this can be used for is partial updates for RESTful API’s, or, to quote the IETF: “This format is also potentially useful in other cases in which it is necessary to make partial updates to a JSON document or to a data structure that has similar constraints (i.e., they can be serialized as an object or an array using the JSON grammar).”

In other words, if you’re building a RESTful API, you’ll probably want to use this.

I figured I’d create a sample project showing how to use this (a Wine Cellar management app).  The full example can be found on my GitHub page.  It contains a sample Web API, a sample MVC 6 client and a sample MVC 5 client.

Let’s start with the Web API side:

 [HttpPatch("{id}")]
 public IActionResult Patch(int id, 
     [FromBody]JsonPatchDocument<BottleOfWine> bottleOfWinePatchDocument)
 {
     // find the bottle with the correct id
     var bottles = Datastore.WineStore.BottlesOfWine;
     var correctBottle = bottles.FirstOrDefault<BottleOfWine>(b => b.Id == id);
        
     if (correctBottle == null)        
         return HttpNotFound();

     // apply patch document
     bottleOfWinePatchDocument.ApplyTo(correctBottle);

     return new ObjectResult(correctBottle);
 }

JsonPatchDocument is defined in the Microsoft.AspNet.JsonPatch namespace.  The action, attributed with “HttpPatch”, receives such a document as a parameter.  A JsonPatchDocument can be looked at as a change set that has to be executed on a resource.  Here’s an example of a patch document:

 [
     { "op": "test", "path": "/a/b/c", "value": "foo" },
     { "op": "remove", "path": "/a/b/c" },
     { "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
     { "op": "replace", "path": "/a/b/c", "value": 42 },
     { "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
     { "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
   ]

What the integration of this into ASP .NET does is ensure you don’t have to create it nor run through it to apply it manually – it offers type-safe creation of a patch document (at MVC side) and a way to apply it to your resource at Web API side.

In the Patch method, you then get the object you want to apply the patch document to, and call the ApplyTo method.  That’s it – your change set has now been applied to the object.  What you want to do after this depends on your needs, but often you’ll want to save the changes to your repository.

On to the client: I’ve added an MVC 6 client to the mix, in which we can change some of the details of the bottles of wine in our cellar:

image

As you can see, not all fields are editable – this thus makes a fine example for a patch request.  In the controller’s Edit (HttpPost) method, the model used by the edit view gets passed in.  It’s from that model that we’ll want to create a Patch document to send to your API.

[HttpPost]
public async Task<ActionResult> Edit(int id, BottleOfWine model)
{
    try
    {
        // we're only showing 2 properties that can be edited => not a 
        // full edit, so we'll want to use JsonPatch

        // create a JsonPatch document 
        JsonPatchDocument<BottleOfWine> patchDoc =
        new JsonPatchDocument<BottleOfWine>();
        patchDoc.Replace(b => b.Grape, model.Grape);
        patchDoc.Replace(b => b.Year, model.Year);

        // serialize
        var serializedPatchDoc = JsonConvert.SerializeObject(patchDoc);

        // create the patch request
        var method = new HttpMethod("PATCH");
        var request = new HttpRequestMessage(method, 
            "http://localhost:1735/api/bottlesofwine/" + id)
        {
            Content = new StringContent(serializedPatchDoc,
            System.Text.Encoding.Unicode, "application/json")
        };

        // send it, using an HttpClient instance
        HttpClient client = new HttpClient();
        var result = await client.SendAsync(request);
        
        if (result.IsSuccessStatusCode)
        {
            return RedirectToAction("Index");
        }

        return View(result.StatusCode);
    }
    catch
    {
        return View();
    }
}

As there’s only two fields that can be edited, it’s those two fields we’ll want to create a patch document for.   In this case, we’re only replacing values, but you can also use this to add (eg: to an array), move, copy, … fields – all those methods from the JsonPatch standard are supported.

After that, it’s a matter of creating a new HttpRequest & sending it.  This will result in the Patch method on our API getting executed, and the document being applied to the BottleOfWine resource.  And if you fully want to follow the standard, you can send it with media type “application/json-patch+json” as content type as well, as long as you add support for that media type to the input formatter at API side.

But what if you’re using an older client version?  For example: your API is built using the new ASP .NET, but your client application is built with the previous MVC version?  Or you’ve got a Windows Phone client, or a Windows Store client?  Well, that’ll work as well – just add the Marvin.JsonPatch NuGet package to your solution to add JsonPatch support (details: https://github.com/KevinDockx/JsonPatch).  I’ve included an example MVC 5 client to show how to do this.  This also works the other way around: you can use a new ASP .NET MVC 6 client with an “old” Web API backend that implements JsonPatch support through Marvin.JsonPatch.

All of this is of course still work in progress; more features will be added before the final release of ASP .NET.  But if you already need to use it now: this is how.

Happy coding! :)

 Tweet about this on TwitterShare on LinkedInShare on Facebook

One Comment

Andrew V

Awesome work. Is there anyway to have modelstate validation work with the JsonPatchDocument?

Comments are closed.