17

假设我有这个模型

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

我将它作为 json 数据作为补丁请求发送到我的控制器。这只是切换复选框的动作。我认为我只想将其发送到我的服务器而不是整个模型是有道理的。

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

为了正确处理这个简单的 json 补丁请求,我的 WebApi 控制器需要什么样的外观?我应该为此使用 web api,还是应该对 mvc 使用更多 rpc 风格的方法?

这似乎是一件非常基本的事情,但我似乎做错了!我想我可能需要在我的控制器方法中使用不同的参数,但我不确定。

感谢您的时间。

4

4 回答 4

12

将方法更改为 PATCH 不会以任何方式更改 Web API 行为。没有用于进行部分更新的内置机制。这么长时间没有 PATCH 方法的原因之一是没有普遍存在的媒体类型可以将补丁应用于资源。

其次,您要求 Web API 为您进行对象序列化,因此没有应用部分更新对象的概念。会有很多约定要达成一致,空值是什么意思,空值是什么意思,我怎么说“不要更新这个日期时间”。相关对象、子项呢?你如何导致子项目被删除?除非 CLR 团队实现某种类型的概念,该类型只包含来自另一个类型的成员的子集,否则部分更新和对象序列化不会很好地结合在一起。

Aliostad 提到了 UpdateModel,这在从 HTML 表单更新时是可能的,因为媒体类型application/x-www-form-urlencoded明确允许任意一组名称值对。没有进行“对象序列化”。它只是将表单中的名称与 Model 对象上的名称匹配。

对于我自己,我创建了一种新的媒体类型,用于进行部分更新,它的工作方式类似于表单,但更先进的是它可以处理分层数据并维护更新的顺序。

于 2012-04-26T12:36:32.543 回答
12

您可以在 OData 预发布 Nuget 包中找到 PATCH 功能:Microsoft.AspNet.WebApi.OData

可以在有关ASP.NET Web API 中的 OData 支持的博客文章的部分更新(PATCH 请求)部分中找到如何使用它来创建处理 PATCH 的操作的信息。

于 2012-09-18T10:21:05.497 回答
2

ASP.NET Web API 似乎缺少UpdateModel,TryUpdateModel等。

在 ASP.NET MVC 中,您可以使用它们来达到预期的效果。我在 ASP.NET Web Stack 中创建了一个您可以投票的工作项,如果它获得足够的票数,它将被实施。

于 2012-04-26T12:28:59.737 回答
1

我在我的项目中使用了Microsoft.AspNet.WebApi.OData,但在使用 JSON 时遇到了一些问题(在我的情况下使用数字)。此外,OData 包有一些依赖项,在我看来,这些依赖项对于单个功能来说太大了(约 7MB 与所有依赖项)。

所以我开发了一个简单的库来满足你的要求:SimplePatch

如何使用

使用以下命令安装包:

Install-Package SimplePatch

然后在你的控制器中:

[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();
}

该库也支持大量补丁:

[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();
}

如果使用实体框架,则只需在调用Patch方法后添加两行代码:

entity.Patch(entityToPatch);

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

Patch此外,您可以在调用该方法 时排除一些要更新的属性。Global.asax 或 Startup.cs

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

当您使用实体并且不想创建模型时,这很有用。

于 2017-07-31T07:56:53.377 回答