I currently have a Web API that implements a RESTFul API. The model for my API looks like this:
public class Member
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Created { get; set; }
public DateTime BirthDate { get; set; }
public bool IsDeleted { get; set; }
}
I've implemented a PUT
method for updating a row similar to this (for brevity, I've omitted some non-relevant stuff):
[Route("{id}")]
[HttpPut]
public async System.Threading.Tasks.Task<HttpResponseMessage> UpdateRow(int id,
[FromBody]Models.Member model)
{
// Do some error checking
// ...
// ...
var myDatabaseEntity = new BusinessLayer.Member(id);
myDatabaseEntity.FirstName = model.FirstName;
myDatabaseEntity.LastName = model.LastName;
myDatabaseEntity.Created = model.Created;
myDatabaseEntity.BirthDate = model.BirthDate;
myDatabaseEntity.IsDeleted = model.IsDeleted;
await myDatabaseEntity.SaveAsync();
}
Using PostMan, I can send the following JSON and everything works fine:
{
firstName: "Sara",
lastName: "Smith",
created: '2018/05/10",
birthDate: '1977/09/12",
isDeleted: false
}
If I send this as my body to http://localhost:8311/api/v1/Member/12
as a PUT request, the record in my data with ID of 12 gets updated to what you see in the JSON.
What I would like to do though is implement a PATCH verb where I can do partial updates. If Sara gets married, I would like to be able to send this JSON:
{
lastName: "Jones"
}
I would like to be able to send just that JSON and update JUST the LastName
field and leave all the other fields alone.
I tried this:
[Route("{id}")]
[HttpPatch]
public async System.Threading.Tasks.Task<HttpResponseMessage> UpdateRow(int id,
[FromBody]Models.Member model)
{
}
My problem is that this returns all the fields in the model
object (all of them are nulls except the LastName
field), which makes sense since I am saying I want a Models.Member
object. What I would like to know is if there is a way to detect which properties have actually been sent in the JSON request so I can update just those fields?
PATCH
operations aren't usually defined using the same model as thePOST
orPUT
operations exactly for that reason: How do you differentiate between anull
, and adon't change
. From the IETF:You can look here for their
PATCH
suggestion, but sumarilly is:@Tipx's answer re using
PATCH
is spot on, but as you've probably already found, actually achieving that in a statically typed language like C# is a non-trivial exercise.In the case where you're using a
PATCH
to represent a set of partial updates for a single domain entity (e.g. to update the first name and last name only for a contact with many more properties) you need to do something along the lines of looping each instruction in the 'PATCH' request and then applying that instruction to an instance of your class.Applying an individual instruction will then comprise of
For Web API 2 on the full .NET Framework the JSONPatch github project looks to make a stab at providing this code, although it doesn't look like there's been a lot of development on that repo recently and the readme does state:
Things are simpler on .NET Core as that has a set of functionality to support this in the
Microsoft.AspNetCore.JsonPatch
namespace.The rather useful jsonpatch.com site also lists out a few more options for Patch in .NET:
I need to add this functionality to an existing Web API 2 project of ours, so I'll update this answer if I find anything else that's useful while doing that.
I hope this helps using Microsoft JsonPatchDocument:
.Net Core 2.1 Patch Action into a Controller:
Node Model class:
A valid Patch JSon to update just the "full_name" and the "node_id" properties will be an array of operations like:
As you can see "op" is the operation you would like to perform, the most common one is "replace" which will just set the existing value of that property for the new one, but there are others:
Here is an extensions method I built based on the Patch ("replace") specification in C# using reflection that you can use to serialize any object to perform a Patch ("replace") operation, you can also pass the desired Encoding and it will return the HttpContent (StringContent) ready to be sent to httpClient.PatchAsync(endPoint, httpContent):
}
Noticed that tt also uses this class I created to serialize the PatchObject using DataContractJsonSerializer:
A C# example of how to use the extension method and invoking the Patch request using HttpClient:
You can take a look at my two samples on GitHub:
https://github.com/ernestleyva/SampleRestfulWebApi
https://github.com/ernestleyva/HttpClientApiService
Thanks