【问题标题】:How to implement JsonPatch in .NET Core 3.0 Preview 9 correctly?如何在 .NET Core 3.0 Preview 9 中正确实现 JsonPatch?
【发布时间】:2020-01-14 19:18:25
【问题描述】:

我正在尝试在 .NET Core 3.0 Preview 9 web api 上实现 JsonPatch。

型号:

public class TestPatch
{
    public string TestPath { get; set; }
}

web api 端点:

[HttpPatch()]
public async Task<IActionResult> Update([FromBody] JsonPatchDocument<TestPatch> patch)
{
   ...........
   return Ok();
}

JSON 负载:

[
    {
        "op" : "replace",
        "path" : "/testPath",
        "value" : "new value"
    }
]

通过 Postman 使用 PATCH,我收到此错误:

{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|492c592-4f7de4d16a32b942.",
"errors": {
    "$": [
        "The JSON value could not be converted to Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1[Test.Models.TestPatch]. Path: $ | LineNumber: 0 | BytePositionInLine: 1."
    ]
}
}

这是来自 Postman 的完整请求/响应

PATCH /api/helptemplates HTTP/1.1
Content-Type: application/json
User-Agent: PostmanRuntime/7.16.3
Accept: */*
Cache-Control: no-cache
Postman-Token: a41813ea-14db-4664-98fb-ee30511707bc
Host: localhost:5002
Accept-Encoding: gzip, deflate
Content-Length: 77
Connection: keep-alive
[
{
"op" : "replace",
"path" : "/testPath",
"value" : "new value"
}
]
HTTP/1.1 400 Bad Request
Date: Thu, 12 Sep 2019 21:13:08 GMT
Content-Type: application/problem+json; charset=utf-8
Server: Kestrel
Transfer-Encoding: chunked
{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"One or more validation errors occurred.","status":400,"traceId":"|492c593-4f7de4d16a32b942.","errors":{"$":["The JSON value could not be converted to Microsoft.AspNetCore.JsonPatch.JsonPatchDocument`1[Test.Models.TestPatch]. Path: $ | LineNumber: 0 | BytePositionInLine: 1."]}}

JsonPatch 参考:

<PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="3.0.0-preview9.19424.4" />

我的代码有什么问题?

谢谢。

【问题讨论】:

标签: asp.net .net-core .net-core-3.0 asp.net-core-3.0


【解决方案1】:

使用Microsoft.AspNetCore.Mvc.NewtonsoftJson 包启用对JsonPatch 的支持。要启用此功能,应用必须:

  • 安装Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet 包。

  • 更新项目的 Startup.ConfigureServices 方法以包含 致电AddNewtonsoftJson

services
    .AddControllers()
    .AddNewtonsoftJson();

AddNewtonsoftJson兼容MVC服务注册方式:

  • AddRazorPages
  • AddControllersWithViews
  • AddControllers

但是如果你使用的是asp.net core 3.x,那么

AddNewtonsoftJson 替换了用于格式化所有 JSON 内容的基于System.Text.Json 的输入和输出格式化程序。要使用Newtonsoft.Json 添加对JsonPatch 的支持,同时保持其他格式化程序不变,请按如下方式更新项目的Startup.ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.InputFormatters.Insert(0, GetJsonPatchInputFormatter());
    });
}

private static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter()
{
    var builder = new ServiceCollection()
        .AddLogging()
        .AddMvc()
        .AddNewtonsoftJson()
        .Services.BuildServiceProvider();

    return builder
        .GetRequiredService<IOptions<MvcOptions>>()
        .Value
        .InputFormatters
        .OfType<NewtonsoftJsonPatchInputFormatter>()
        .First();
}

上述代码需要引用Microsoft.AspNetCore.Mvc.NewtonsoftJson 和以下 using 语句:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System.Linq;

上面的简要说明和文档可以在link中找到

【讨论】:

  • 这个解决方案有效,但我有一个小问题。我有一个 OutputFormatters 设置 XmlDataContractSerializerOutputFormatter 来支持 XML 响应。在您的代码更改后,所有请求都必须使用 application/json 明确提及 Accept 标头。否则默认返回 XML。如何将 Json 设置为默认输出格式?
  • services.AddMvc(options => options.EnableEndpointRouting = false) .AddMvcOptions(o => { o.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter()); o.InputFormatters.Insert(0, GetJsonPatchInputFormatter( )); }).AddNewtonsoftJson();
  • 那是因为你的配置顺序。您需要添加services.AddMvc().AddNewtonsodtJson().AddXmlDataContractSerializerFormatters。请参阅我的 EnterpriseArchitecture 存储库以供参考。您需要交换注册输出格式化程序的顺序。在Asp.Net Core 3.1 中,您只需添加AddXmlDataContractSerializerFormatters。请从AddMvcOptions中删除XmlDataContractSerializerOutputFormatter
  • 非常感谢。抱歉迟到了:)
  • 注意:截至目前,文档不完整。如果您将System.Text.Json 与您的API 一起使用,而将NewtonSoft.Json 用于JSON PATCH 文档,则客户端必须发送Content-Type application/json-patch+json 而不是application/json,否则它不起作用
【解决方案2】:

这个答案适用于 3.1,但我认为它也适用于 3.0.... asp.net core 3.x 中默认的 json 解析器不如 NewtonsoftJson 完整,所以在微软实现某个功能之前使用它。

将此 nuget 包添加到您的项目中: Microsoft.AspNetCore.Mvc.NewtonsoftJson

然后在startup.cs中添加这个using语句:

using Newtonsoft.Json.Serialization;

... 然后更改您的 ConfigureService() 以在 startup.cs 中包含 NewtonsoftJson 格式化程序:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(setupAction =>
    setupAction.ReturnHttpNotAcceptable = true
   ).AddXmlDataContractSerializerFormatters().AddNewtonsoftJson(setupAction =>
   setupAction.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());
   //...
}

您可能还必须将 Accept set to application/json 添加到您的请求中,以防止它们返回 XML。

希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-12
    • 2019-06-12
    • 2019-08-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多