17

Assume i have this model

public partial class Todo
{
    public int id { get; set; }
    public string content { get; set; }
    public bool done { get; set; }
}

And i send this as json data to my controller as a patch request. This is mearly the action of toggeling a checkbox. I think it makes sence that i only want to sent that to my server, and not the entire model.

{ "id":1, "done" : true }

What does my WebApi controller need to look like in order to correctly process this, simple, json patch request ? Should i be using web api for this, or should i use a more rpc styled approach with mvc ?

It seems like a very basic thing to do, but i can't seem to get it right ! I think i might need to use a different parameter in my controller method, but i'm not sure.

Thank you for your time.

2
  • 2
    You can use JsonPatch which is supported by ASP.NET, ASP.NET Core and PCL for Xamarin. github.com/KevinDockx/JsonPatch. Good article can be found in here benfoster.io/blog/aspnet-core-json-patch-partial-api-updates Commented Nov 10, 2016 at 5:13
  • That's a different problem I think. My model isn't json based. How the actual state update is going to happen should be up to the implementer. I don't want to have to translate some RFC spec to a SQL query or entity mutations. Commented Nov 10, 2016 at 5:18

4 Answers 4

12

You can find PATCH feature in the OData pre-release Nuget package: Microsoft.AspNet.WebApi.OData.

Information how you can use it to create an action for handling PATCH can be found in the Partial Updates (PATCH requests) section of the blog post about OData support in ASP.NET Web API.

Sign up to request clarification or add additional context in comments.

1 Comment

Look at this sample for doing patch through the json.net serializer without depending on oData wire formats - aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/…
12

Changing the method to PATCH doesn't change Web API behaviour in any way. There is no built in mechanism for doing partial updates. One of the reasons there was no PATCH method for so long is that there is no ubiquitous media type for applying patches to resources.

Secondly, you are asking Web API to do object serialization for you so there just is no such concept of applying a partially updated object. There would be so many conventions to agree on, what does a null value mean, what about an empty value, how do I say "don't update this DateTime". What about related objects, child items? How do you cause a child item to be deleted? Unless the CLR team implements some concept of a type that only contains a subset of members from another type, partial updates and object serialization are not going to go well together.

Aliostad mentions UpdateModel and that is possible when updating from a HTML form because the media type application/x-www-form-urlencoded explicitly allows for an arbitrary set of name value pairs. There is no "object serialization" going on. It is just a match of names from the form being matched to names on the Model object.

For myself, I created a new media type I use to do partial updates that works like a form but is more advanced in that it can handle hierarchial data and it maintains an order to the updates.

3 Comments

+1. Can this media type formatter of yours be included in the web api contrib? This is a common scenario and I can imagine such feature is frequently requested.
Thank you for your answer, what would be the right approach if i wanted to updated just one field of my model ( the 'done' checkbox field in this scenario ) ? Should i abandon web api? how can i do this without losing my model validation ? It's hard to believe that such a simple action would not be possible with "the latests of the latests" in asp.net technologies
@Aliostad I'm working on publishing a spec and parser for my EventSequence media type. It will work just fine with Web Api. For a bit more info, I did a quick screencast on it at RESTFest which is here vimeo.com/15564107
2

ASP.NET Web API seems to be missing UpdateModel, TryUpdateModel, etc.

In ASP.NET MVC, you could use them to achieve the desired effect. I have created a work item in ASP.NET Web Stack which you can vote for and if it gets enough votes, it will be implemented.

Comments

1

I used Microsoft.AspNet.WebApi.OData for my project and I had some problems working with JSON (working with numbers in my case). Also, the OData package has some dependencies which, from my point of view, are too big for a single feature (~7MB with all dependecies).

So I developed a simple library which do what you are asking for: SimplePatch.

How to use

Install the package using:

Install-Package SimplePatch

Then in your controller:

[HttpPatch]
public IHttpActionResult PatchOne(Delta<Todo> todo)
{
    if (todo.TryGetPropertyValue(nameof(Todo.id), out int id)) {
        // Entity to update (from your datasource)
        var todoToPatch = Todos.FirstOrDefault(x => x.id == id);
        if (todoToPatch == null) return BadRequest("Todo not found");

        todo.Patch(todoToPatch);     

        // Now todoToPatch is updated with new values            
    } else {
        return BadRequest();
    }     

    return Ok();
}

The library support massive patch too:

[HttpPatch]
public IHttpActionResult PatchMultiple(DeltaCollection<Todo> todos)
{
    foreach (var todo in todos)
    {
        if (todo.TryGetPropertyValue(nameof(Todo.id), out int id))
        {
            // Entity to update (from your datasource)
            var entityToPatch = Todos.FirstOrDefault(x => x.id == Convert.ToInt32(id));
            if (entityToPatch == null) return BadRequest("Todo not found (Id = " + id + ")");

            person.Patch(entityToPatch);
        }
        else
        {
            return BadRequest("Id property not found for a todo");
        }
    }

    return Ok();
}

If you use Entity Framework, you have to add only two lines of code after the call to the Patch method:

entity.Patch(entityToPatch);

dbContext.Entry(entityToPatch).State = EntityState.Modified;
dbContext.SaveChanges();

Furthermore, you can exclude some properties to be updated when the Patch method is called. Global.asax or Startup.cs

DeltaConfig.Init((cfg) =>
{
    cfg.ExcludeProperties<Todo>(x => x.id);
});

This is usefull when you are working with an entity and you don't want to create a model.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.