【问题标题】:Implementing JSON Merge Patch in ASP.NET Core WebAPI在 ASP.NET Core WebAPI 中实现 JSON 合并补丁
【发布时间】:2018-02-22 21:42:09
【问题描述】:

我有兴趣在我的 ASP.NET Core WebAPI 中添加对部分更新的支持,我只更新调用者提供的资源的属性,而排除的属性保持不变。

对于上下文,假设我有一个可以描述如下的资源:

GET /users/1
{
    title: "Mister",
    firstName: "Frederick",
    middleName: "McFeely",
    lastName: "Rodgers"
}

如果我想允许消费者将存储在firstName 属性中的值从“Frederick”单独更改为“Fred”,我应该能够公开一个支持JSON Merge Patch@987654328 的PATCH 端点@,就像这样:

PATCH /users/1
Content-Type: application/merge-patch+json
{
    firstName: "Fred"
}

但是,我认为没有简单的方法可以知道 firstName 是唯一正在更新的属性。例如,如果我要创建一个接受PATCH 动词的控制器,它可以像这样搭建:

[Route("users")]
public class UsersController : Controller {

    [HttpPatch("{userId:int}")]
    public User Patch([FromRoute] int userId, [FromBody] User user) {

        // How do I know which properties were set on User at this point?

    }

}

public class User {

    public String Title { get; set; }
    public String FirstName { get; set; }
    public String MiddleName { get; set; }
    public String LastName { get; set; }

}

但我不知道如何提取在 JSON 对象上定义了哪些属性的键,然后再将其作为User 水合并传递给我的控制器。我不能假设 null 的值意味着一个属性被排除在外,因为调用者可能明确地将一个可选属性设置为 null。

编辑

我知道Microsoft.AspNetCore.JsonPatch 库。不幸的是,这期望调用者使用“[description of changes]”来定义PATCH,如RFC 5789 中所述,我觉得这既不直观又冗长。我指的是RFC 7396中定义的“JSON Merge Patch”。

【问题讨论】:

    标签: c# asp.net-mvc asp.net-web-api asp.net-core asp.net-core-webapi


    【解决方案1】:

    对于简单类型,我找到了一个非常简单的解决方案,使用 JObjects 的Newtonsoft.Json merge

    public static T Patched<T>(T source, JObject patch) where T : class
    {
        var sourceObject = JObject.FromObject(source);
        sourceObject.Merge(patch, new JsonMergeSettings() {MergeArrayHandling = MergeArrayHandling.Union});
        return sourceObject.ToObject<T>();
    }
    
    public static T Patched<T>(T source, string patchCode) where T : class
    {
        return Patched<T>(source, JObject.Parse(patchCode));
    }
    

    希望这可以帮助搜索此主题并寻找没有外部包的简单解决方案的人。

    【讨论】:

      【解决方案2】:

      我找到了一个有效的库:https://github.com/Morcatko/Morcatko.AspNetCore.JsonMergePatch

      [HttpPatch]
      [Consumes(JsonMergePatchDocument.ContentType)]
      public void Patch([FromBody] JsonMergePatchDocument<Model> patch)
      {
          ...
          patch.ApplyTo(backendModel);
          ...
      }
      

      或使用patch.JsonPatchDocument.Operations 手动浏览补丁请求字段。

      【讨论】:

        【解决方案3】:

        看起来,对于合并补丁,您将不得不等待 odata 支持。

        目前处于测试阶段,支持与 Delta 类的合并语义。

        https://www.nuget.org/packages/Microsoft.AspNetCore.OData/

        【讨论】:

        • 很好,这看起来很有希望。我会在它发布时回过头来讨论它。
        • 在 7.0.0 中反序列化实体时,实体 ID 和集合存在一些问题:(
        【解决方案4】:

        【讨论】:

          【解决方案5】:

          要打补丁,你必须定义 PatchDocument。

          更多信息你可以找到PatchDocument

          方法示例。

           [HttpPatch("{userId:int}")]   
           public IActionResult UserPatch(int userId, [FromBody] JsonPatchDocument<User> patchDocument) {
          
              var user = new User();
              // Because it comes from url.
              user.Id = userId;
              patchDocument.ApplyTo(user);
          
              // Here you call context or repository to save.
          
            }
          

          文档示例。

          [
            { "op": "replace", "path": "/firstName", "value": "boo" },
          ]
          

          这会将用户模型中的 firstName 字段更新为“boo”。

          【讨论】:

          • 您好,感谢您的回答。不幸的是,您将the original idea of PATCH 与我正在寻找的东西混淆了:JSON Merge PATCH。它们使用相同的动词,但后者是 RFC 中定义的特定内容类型。
          猜你喜欢
          • 1970-01-01
          • 2018-05-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-10
          相关资源
          最近更新 更多