简体   繁体   English

在 ASP.NET Core WebAPI 中实现 JSON Merge Patch

[英]Implementing JSON Merge Patch in ASP.NET Core WebAPI

I am interested in adding support for partial updates in my ASP.NET Core WebAPI where I only update the properties on a resource that the caller provided, leaving excluded properties unchanged.我有兴趣在我的 ASP.NET Core WebAPI 中添加对部分更新的支持,我只更新调用者提供的资源上的属性,而排除的属性保持不变。

For context, imagine I have a resource that can be described as follows:对于上下文,假设我有一个可以描述如下的资源:

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

If I wanted to allow consumers to change the value stored in the firstName property from "Frederick" to "Fred" in isolation, I should be able to expose a PATCH endpoint that supports the JSON Merge Patch Content-Type , like so:如果我想允许消费者将存储在firstName属性中的值从“Frederick”单独更改为“Fred”,我应该能够公开一个支持JSON Merge Patch Content-TypePATCH端点,如下所示:

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

However, I see no easy way for me to know that firstName is the only property being updated.但是,我认为没有简单的方法可以知道firstName是唯一正在更新的属性。 For example, if I were to make a controller that accepted PATCH verbs, it could be scaffolded like this:例如,如果我要制作一个接受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; }

}

But I don't see how I can extract which properties' had keys defined on the JSON object before it was hydrated as a User and passed to my controller.但是我不知道如何在 JSON 对象作为User水合并传递给我的控制器之前提取在 JSON 对象上定义了哪些属性的键。 I cannot assume a value of null to mean a property was excluded as the caller could be explicitly setting an optional property to null.我不能假设null值意味着一个属性被排除在外,因为调用者可以将一个可选属性显式设置为 null。

Edit编辑

I am aware of the Microsoft.AspNetCore.JsonPatch library.我知道Microsoft.AspNetCore.JsonPatch库。 This, unfortunately, expects the caller to use the "[description of changes]" to define a PATCH as described in RFC 5789 , which I find unintuitive and verbose.不幸的是,这期望调用者使用“[更改说明]”来定义一个PATCH ,如RFC 5789 中所述,我发现它不直观且冗长。 I am referring to the "JSON Merge Patch" defined in RFC 7396 .我指的是RFC 7396 中定义的“JSON Merge Patch”。

I found a library that works: https://github.com/Morcatko/Morcatko.AspNetCore.JsonMergePatch我找到了一个有效的库: https : //github.com/Morcatko/Morcatko.AspNetCore.JsonMergePatch

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

Or use patch.JsonPatchDocument.Operations to walk through patch request fields manually.或者使用patch.JsonPatchDocument.Operations手动遍历补丁请求字段。

It appears like, for merge patch you will have to wait for odata support.看起来,对于合并补丁,您将不得不等待 odata 支持。

It is in beta at the moment and supports the merge semantics with the Delta<> class.它目前处于测试阶段,并支持与 Delta<> 类的合并语义。

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

for simple types, I found a very simple solution using Newtonsoft.Json merge of JObjects:对于简单类型,我使用Newtonsoft.Json 合并JObjects 找到了一个非常简单的解决方案:

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

Hope this helps someone searching for this topic and looking for a simple solution without external packages.希望这可以帮助搜索此主题并寻找没有外部软件包的简单解决方案的人。

For doing a patch, you have to define PatchDocument.要打补丁,您必须定义 PatchDocument。

More about it you can find PatchDocument更多关于它你可以找到PatchDocument

Example of method.方法示例。

 [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.

  }

Example of document.文档示例。

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

That will update firstName field to 'boo' in user model.这会将用户模型中的 firstName 字段更新为“boo”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM