【问题标题】:asp web api patch implementationasp web api补丁实现
【发布时间】:2012-05-07 03:35:34
【问题描述】:

假设我有这个模型

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 风格的方法?

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

感谢您的宝贵时间。

【问题讨论】:

标签: c# asp.net rest model asp.net-web-api


【解决方案1】:

ASP.NET Web API 似乎缺少UpdateModelTryUpdateModel 等。

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

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

    • +1。你的这个媒体类型格式化程序可以包含在 web api contrib 中吗?这是一个常见的场景,我可以想象这样的功能经常被请求。
    • 感谢您的回答,如果我只想更新模型的一个字段(本场景中的“完成”复选框字段),正确的方法是什么?我应该放弃 web api 吗?如何在不丢失模型验证的情况下做到这一点?很难相信在 asp.net 技术中使用“最新的最新消息”不可能实现如此简单的操作
    • @Aliostad 我正在为我的 EventSequence 媒体类型发布规范和解析器。它适用于 Web Api。有关更多信息,我在 RESTFest 上对其进行了快速截屏,此处为 vimeo.com/15564107
    【解决方案3】:

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

    有关如何使用它来创建用于处理 PATCH 的操作的信息,请参阅关于 OData support in ASP.NET Web API 的博客文章的部分更新(PATCH 请求)部分。

    【讨论】:

    【解决方案4】:

    我在我的项目中使用了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);
    });
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-11
      • 2014-10-25
      • 1970-01-01
      • 1970-01-01
      • 2016-10-31
      相关资源
      最近更新 更多