在 ASP.NET Core WebAPI 中实现 JSON 合并补丁

Posted

技术标签:

【中文标题】在 ASP.NET Core WebAPI 中实现 JSON 合并补丁【英文标题】:Implementing JSON Merge Patch in ASP.NET Core WebAPI 【发布时间】: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”。

【问题讨论】:

【参考方案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】:

您可能正在寻找的是 ASP.Net Core JsonPatchDocument

https://github.com/aspnet/JsonPatch

https://docs.microsoft.com/en-us/aspnet/core/api/microsoft.aspnetcore.jsonpatch

【讨论】:

【参考方案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 中定义的特定内容类型。

以上是关于在 ASP.NET Core WebAPI 中实现 JSON 合并补丁的主要内容,如果未能解决你的问题,请参考以下文章

在 ASP.NET Core 2.1 Web API 中实现分页

使用 Microsoft System.IdentityModel.Tokens.Jwt 在 Asp.net WebApi 中实现 JWT 身份验证

在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务

使用 Swagger 在 ASP.NET Core 中实现 OAuth

如何在ASP.NET Core中实现一个基础的身份认证

在 ASP.Net Core 中实现 ADO 连接