Reputation: 6158
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.
Upvotes: 19
Views: 7797
Reputation: 156
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.
Upvotes: 4
Reputation: 4049
I found a library that works: https://github.com/Morcatko/Morcatko.AspNetCore.JsonMergePatch
[HttpPatch]
[Consumes(JsonMergePatchDocument.ContentType)]
public void Patch([FromBody] JsonMergePatchDocument<Model> patch)
{
...
patch.ApplyTo(backendModel);
...
}
Or use patch.JsonPatchDocument.Operations
to walk through patch request fields manually.
Upvotes: 6
Reputation: 1116
It appears like, for merge patch you will have to wait for odata support.
It is in beta at the moment and supports the merge semantics with the Delta<> class.
https://www.nuget.org/packages/Microsoft.AspNetCore.OData/
Upvotes: 1
Reputation: 5459
What you might be looking for is ASP.Net Core JsonPatchDocument
https://github.com/aspnet/JsonPatch
https://learn.microsoft.com/en-us/aspnet/core/api/microsoft.aspnetcore.jsonpatch
Upvotes: -3
Reputation: 462
For doing a patch, you have to define PatchDocument.
More about it you can find PatchDocument
Example of method.
[HttpPatch("{userId:int}")]
public IActionResult UserPatch(int userId, [FromBody] JsonPatchDocument<User> patchDocument) {
var user = new User();
// Because it comes from url.
user.Id = userId;
patchDocument.ApplyTo(user);
// Here you call context or repository to save.
}
Example of document.
[
{ "op": "replace", "path": "/firstName", "value": "boo" },
]
That will update firstName field to 'boo' in user model.
Upvotes: -3