You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core 3.1 API中JsonPatch无法正常工作的技术求助

解决ASP.NET Core 3.1中JsonPatchDocument适配自定义请求结构的问题

你遇到的序列化错误,核心原因是默认的JsonPatchDocument<T>期望直接接收操作数组作为请求体(比如[{"op":"replace",...},...]),但你的契约把操作数组包裹在了items字段下,再加上System.Text.Json和Newtonsoft.Json的配置冲突,导致自动绑定逻辑无法正确识别嵌套的数组结构。下面是具体的解决步骤:


第一步:确保Newtonsoft.Json的Patch支持正确配置

首先确认你已经安装了Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet包(ASP.NET Core 3.1默认不包含这个,需要手动安装)。然后在Startup.cs中,不仅要定义格式化器方法,还要把它添加到Mvc的输入格式化器集合中,确保优先使用Newtonsoft的处理逻辑:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        // 将自定义的Newtonsoft格式化器插入到最前面,确保优先处理Patch请求
        options.InputFormatters.Insert(0, GetJsonPatchInputFormatter());
    })
    .AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.DateParseHandling = Newtonsoft.Json.DateParseHandling.DateTimeOffset;
    });
}

// 你已有的格式化器方法保持不变
private static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter()
{
    var builder = new ServiceCollection()
        .AddLogging()
        .AddMvc()
        .AddNewtonsoftJson(_ => _.SerializerSettings.DateParseHandling = Newtonsoft.Json.DateParseHandling.DateTimeOffset)
        .Services.BuildServiceProvider();

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

第二步:调整请求类与绑定逻辑

因为你的请求结构是外层对象包含items数组,而JsonPatchDocument<T>本质是操作的集合,这里提供两种可行的适配方式:

方式一:强类型请求类+手动转换(推荐)

先定义匹配请求结构的强类型类,再手动将操作列表转换为JsonPatchDocument<T>,保证类型安全:

// 匹配请求中单个Patch操作的类
public class PatchOperation
{
    public string Op { get; set; }
    public string Path { get; set; }
    public object Value { get; set; }
}

// 你的请求契约类
public class UpdateCustomerRequest
{
    public List<PatchOperation> Items { get; set; }
}

然后在控制器方法中,将操作转换为JsonPatchDocument并应用到目标对象:

[HttpPatch("{customerId}")]
[SwaggerOperation("UpdateCustomer")]
[SwaggerResponse((int)HttpStatusCode.MultiStatus)]
public async Task<IActionResult> UpdateCustomer(Guid customerId, [FromBody] UpdateCustomerRequest request)
{
    var patchDoc = new JsonPatchDocument<CustomerPatchDocument>();

    foreach (var op in request.Items)
    {
        switch (op.Op.ToLowerInvariant())
        {
            case "replace":
                // 根据Path匹配对应的属性,这里以Name为例,可扩展其他属性
                if (op.Path.Equals("/name", StringComparison.OrdinalIgnoreCase))
                {
                    patchDoc.Replace(p => p.Name, op.Value.ToString());
                }
                break;
            case "add":
                // 处理add操作逻辑
                break;
            case "remove":
                // 处理remove操作逻辑
                break;
            // 其他操作类型按需添加
        }
    }

    // 示例:将Patch应用到目标对象
    var targetCustomer = new CustomerPatchDocument();
    patchDoc.ApplyTo(targetCustomer);

    // 后续的保存、返回逻辑
    return Ok(targetCustomer);
}

方式二:手动反序列化(快速解决)

如果不想定义额外的操作类,可以直接用JObject接收请求,手动提取items数组并反序列化为JsonPatchDocument<T>:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

[HttpPatch("{customerId}")]
[SwaggerOperation("UpdateCustomer")]
[SwaggerResponse((int)HttpStatusCode.MultiStatus)]
public async Task<IActionResult> UpdateCustomer(Guid customerId, [FromBody] JObject request)
{
    // 提取items数组并反序列化为JsonPatchDocument
    var itemsJson = request["items"].ToString();
    var patchDoc = JsonConvert.DeserializeObject<JsonPatchDocument<CustomerPatchDocument>>(itemsJson);

    // 应用Patch到目标对象
    var targetCustomer = new CustomerPatchDocument();
    patchDoc.ApplyTo(targetCustomer);

    // 后续逻辑
    return Ok(targetCustomer);
}

为什么之前的配置失败?

你之前直接用JsonPatchDocument<CustomerPatchDocument>作为Items属性类型,但默认序列化器会把JsonPatchDocument<T>识别为数组类型,而你的请求中items是对象的一个数组属性,这种嵌套结构无法被自动映射到JsonPatchDocument<T>,因此触发了序列化错误。

内容的提问来源于stack exchange,提问作者user729400

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 21:52:38