【问题标题】:HttpPatch operation with JsonPatchDocument and dynamic objects issueHttpPatch 操作与 JsonPatchDocument 和动态对象问题
【发布时间】:2020-07-28 17:15:37
【问题描述】:

用这个例子来解决以下问题:

https://github.com/dotnet/AspNetCore.Docs/blob/8793347c90ba83166c0efc6cb5fbae60a0e063a7/aspnetcore/web-api/jsonpatch/samples/3.1/api/Controllers/HomeController.cs

我有一个带有补丁操作的控制器,它以参数 a 为参数

JsonPatchDocument patch

然后将补丁应用到类似于示例的动态(扩展)对象

patch.ApplyTo(obj);

但不幸的是,ApplyTo(obj) 因以下错误而死:

StatusCode = 500, Message = 对于“替换”操作,目标 找不到路径“/updateuser/value”指定的位置

【问题讨论】:

    标签: c# asp.net-core-3.1 json-patch


    【解决方案1】:

    您发送的操作json中的路径不是url-path,它是您的目标实体的属性。

    替换:

    {"op":"replace","path":"/updateuser/value","value":"12345"}
    

    与:

    { "op": "replace", "path": "propertyname", "value": "propertyvalue" }
    

    场景:

    假设我们有一个会议现场,与会者想要更新他们之前给演讲者的评论。在这种情况下,我们有一个与会者想要更新的评论对象,在这里我们只需要更新评论部分,所以让我们使用 JSON Patch 来执行此任务。

    有多种方法可以执行补丁操作。这里有两种简单的方法。

    发送 JsonPatchDocument 对象作为参数 在运行时创建 JsonPatchDocument 我们将在本文中看到这两种方法。现在让我们看一些代码。

    我在 SpeakerController 中创建了“PatchReview”操作,并用 [HTTPPATCH] 属性对其进行了修饰。

       [Route("api/[controller]")]
        public class SpeakerController : Controller
        {
            [HttpPatch("{reviewId}")]
            public IActionResult PatchReview([FromBody] JsonPatchDocument<SpeakerReview> patchDocument)
            {
                if (patchDocument == null)
                {
                    return BadRequest();
                }
                //In your application get speaker review object from database base on review Id
                var speakerFromDb = new SpeakerReview { ReviewId = 2, SpeakerId = 101, Rate = 2.5M, Review = "Good speaker.", ReviewDate = DateTime.UtcNow.AddDays(-1)};
     
                //Apply changes to speaker object
                patchDocument.ApplyTo(speakerFromDb);
     
                //update speaker in database and return updated object
                return Ok(speakerFromDb);
            }
        }
    

    此方法从请求正文中接受 JsonPatchDocument 类型的参数/对象,然后我们将获取需要根据请求的 reviewId 更新的实际评论对象,但出于演示目的,我们正在创建虚拟的 SpeakerReview 对象,我们假设它是一个实际对象并且将补丁应用于该对象并最后返回更新的评论。实际上,场景会有所不同,例如测试 null 条件、打补丁后保存评论等。

    第二种方法:

    在上面的动作中,我传递了一个实际的 JsonPatchDocument 对象作为参数,因此您需要记住 Patch 文档的实际格式,但如果您不想记住这些格式,则可以使用内置的相应操作方法来创建运行时的 JsonPatchDocument。

    让我们看一个第二种方法的例子。如果您深入研究 JsonPatchDocument 类,则存在 Replace 方法,您必须在其中传递路径,即 SpeakerReview 对象的目标属性/属性和新值,然后它将返回 JsonPatchDocument 对象,该对象等效于您作为第一种方法的参数传递的对象。现在,您可以借助 ApplyTo 方法将该补丁文档应用到目标对象。所有 JSON Patch 操作都可以使用方法来创建 JsonPatchDocument 对象。

    //Creating JsonPatchDocument
            //Result: [{"op":"replace", "path":"/review", "value":"Good Speaker. I like presentation Style."}]
            var jsonPatch = new JsonPatchDocument<SpeakerReview>().Replace(x => x.Review, "Good Speaker. I like presentation Style.");
     
            //In your application get speaker review object from database base on review Id
            var speakerFromDb = new SpeakerReview
            {
                ReviewId = 2,
                SpeakerId = 101,
                Rate = 2.5M,
                Review = "Good speaker.",
                ReviewDate = DateTime.UtcNow.AddDays(-1)
            };
     
            //Apply changes to speaker object
            jsonPatch.ApplyTo(speakerFromDb);
     
            //update speaker in database and return updated object
            return Ok(speakerFromDb);
    

    这就是修补逻辑的全部内容。让我们看看如何从 POSTMAN 调用这个 API:

    Note: This step is valid only if you used the first technique.
    

    将请求类型更改为 PATCH 并在正文中准备 JSON 对象。确保它是 JSON Patch 文档格式。这里我们只替换了review,所以我在操作数组中传递单个操作对象,但您可以传递多个操作,它将按顺序执行。

    所以基本上使用这个 JSON Patch 文档,我正在发送一个命令,用新值替换评论属性的旧值。

    提示:确保您的补丁请求标头包含“application/json-patch+json” MIME 的 Content-Type,否则您的请求最终会出现 415 Unsupported Media Type 状态代码。

    让我们将 BreakPoint 放入补丁操作并点击发送按钮。

    在这里,您可以比较旧的和新修补的 SpeakerReview 对象的 review 属性的值。在实际应用程序中,现在您需要执行剩余任务,但这里我返回的是更新的对象。

    您可以尝试第二种方法,结果与上述相同。

    这是更新的 SpeakerReview 对象作为响应。

    就是这样。这就是我们在 AspNet Core 应用程序中实现 JSON Patch 的方式。在开始使用 JSON Patch 之前至少阅读一次 JSON Patch 规范(最多需要 30 分钟)将是很好的第一步。您可以在规范的最后找到不同操作的非常好的示例。

    我希望这可以帮助您开始在 AspNet Core 中使用 JSON Patch。

    【讨论】:

    • 仍然是同样的错误,我正在创建像 dynamic obj = new ExpandoObject(); 这样的动态对象,然后执行此操作 if (patch.Operations.Any(o =&gt; o.path.ToLower().Equals("updateuser"))) { obj.updateUser = new ExpandoObject(); obj.updateUser.value = ""; } 希望有所帮助
    • 奇怪的是,如果我将操作从替换更改为添加,我会通过那个问题?!
    • 我已经更新了答案,请根据您的要求查看方法 A 或方法 B。
    • 感谢您的时间和精力 Nadeem,但您的示例不适合将 JsonPatch 应用于动态对象的用例。阅读我最新的 cmets 究竟需要发生什么。明天我将创建一个测试应用程序来确认带有动态对象的 jsonpatch 是否有效。它可能是问题所在的 nuget 包的版本或其他问题。您如何解释,将替换操作更改为使用相同设置的添加操作?
    • 谢谢@Kris。请分享完整代码。我们会找到解决方案或解决方法。
    【解决方案2】:

    将您的运算符从“替换”更改为“添加”。

    试试这个:

    Json 示例:

    [
      {
        "op": "add",
        "path": "/updateuser",
        "value": {
          "orderName": "Order2",
          "orderType": null
        }
      }
    ]
    

    和行动:

        [HttpPatch]
        public IActionResult JsonPatchWithModelState([FromBody]JsonPatchDocument patch)
        {
            dynamic obj = new ExpandoObject();
    
            patch.ApplyTo(obj);
    
            return Ok(obj);
        }
    
            
    

    【讨论】:

    • 我尝试了这些代码并且工作正常。可以放你的json吗?
    • 如果我得到错误快照,我会尽力排除故障。如果您认为它不安全,我向您致以最良好的祝愿。
    • 绰绰有余。现在社区中的任何人都会清楚地了解并尝试帮助您解决问题或提供解决方法。
    • 你能不能参考一下这个文档....benfoster.io/blog/aspnet-core-json-patch-partial-api-updates 和章节添加项目到一个数组。
    • 稍后会谈到这个。阅读文档并更新代码后,您仍然收到错误吗?
    猜你喜欢
    • 2020-11-30
    • 1970-01-01
    • 2012-11-19
    • 1970-01-01
    • 2011-04-02
    • 2012-06-09
    • 1970-01-01
    • 2011-05-29
    • 1970-01-01
    相关资源
    最近更新 更多