asp web api patch implementation

2019-03-14 20:29发布

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.

4条回答
做个烂人
2楼-- · 2019-03-14 20:58

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楼-- · 2019-03-14 21:14

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.

查看更多
Rolldiameter
4楼-- · 2019-03-14 21:16

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.

查看更多
Juvenile、少年°
5楼-- · 2019-03-14 21:19

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.

查看更多
登录 后发表回答