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

如何自定义ASP.NET Core Web API(.NET8)与NSwag的错误响应体

解决.NET Core 8.0中JSON语法错误响应格式不一致的问题

核心原因

请求JSON语法错误属于请求体解析阶段的异常,发生在ASP.NET Core模型绑定之前,此时框架会直接生成默认400响应,不会触发常规的ExceptionMiddleware(因为中间件链路中异常未被抛出,或响应已开始构建)。

解决思路与实现方案

1. 替换默认JSON输入格式化器(推荐)

由于你使用Newtonsoft.Json,可自定义NewtonsoftJsonInputFormatter捕获解析异常,返回自定义格式响应:

  • 创建自定义格式化器继承NewtonsoftJsonInputFormatter:
public class CustomNewtonsoftJsonInputFormatter : NewtonsoftJsonInputFormatter
{
    public CustomNewtonsoftJsonInputFormatter(ILogger logger, JsonSerializerSettings serializerSettings, ArrayPool<char> charPool, ObjectPoolProvider objectPoolProvider, MvcOptions options, MvcNewtonsoftJsonOptions jsonOptions)
        : base(logger, serializerSettings, charPool, objectPoolProvider, options, jsonOptions)
    {
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        try
        {
            return await base.ReadRequestBodyAsync(context);
        }
        catch (JsonReaderException ex)
        {
            // 构造与OpenAPI规范一致的自定义错误响应体
            var errorResponse = new CustomErrorResponse
            {
                StatusCode = StatusCodes.Status400BadRequest,
                Message = "请求JSON格式错误",
                Details = ex.Message
            };

            context.HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
            await context.HttpContext.Response.WriteAsJsonAsync(errorResponse);
            
            return InputFormatterResult.Success(null);
        }
    }
}
  • 在Program.cs中替换默认格式化器:
builder.Services.AddControllers()
    .AddNewtonsoftJson(options =>
    {
        // 保留你的Newtonsoft.Json配置,比如驼峰命名等
        options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    })
    .AddMvcOptions(options =>
    {
        // 移除默认的NewtonsoftJsonInputFormatter
        var defaultFormatter = options.InputFormatters.OfType<NewtonsoftJsonInputFormatter>().FirstOrDefault();
        if (defaultFormatter != null)
        {
            options.InputFormatters.Remove(defaultFormatter);
            
            // 注册自定义格式化器
            var serviceProvider = builder.Services.BuildServiceProvider();
            options.InputFormatters.Add(new CustomNewtonsoftJsonInputFormatter(
                serviceProvider.GetRequiredService<ILogger<NewtonsoftJsonInputFormatter>>(),
                defaultFormatter.SerializerSettings,
                serviceProvider.GetRequiredService<ArrayPool<char>>(),
                serviceProvider.GetRequiredService<ObjectPoolProvider>(),
                options,
                serviceProvider.GetRequiredService<IOptions<MvcNewtonsoftJsonOptions>>().Value
            ));
        }
    });

2. 使用IStartupFilter拦截响应

若不想替换格式化器,可通过IStartupFilter在响应发送前修改内容:

  • 创建自定义StartupFilter:
public class ErrorResponseStartupFilter : IStartupFilter
{
    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    {
        return app =>
        {
            app.Use(async (context, nextMiddleware) =>
            {
                // 先执行后续中间件,获取原始响应
                await nextMiddleware();
                
                // 仅处理JSON请求的400错误
                if (context.Response.StatusCode == StatusCodes.Status400BadRequest 
                    && context.Request.ContentType?.Contains("application/json") == true)
                {
                    // 读取原始响应内容
                    context.Response.Body.Seek(0, SeekOrigin.Begin);
                    var originalContent = await new StreamReader(context.Response.Body).ReadToEndAsync();
                    context.Response.Body.Seek(0, SeekOrigin.Begin);
                    
                    // 判断是否为框架默认的JSON解析错误响应
                    if (!string.IsNullOrEmpty(originalContent) && originalContent.Contains("\"errors\":{\"\":["))
                    {
                        // 构造自定义错误响应
                        var errorResponse = new CustomErrorResponse
                        {
                            StatusCode = StatusCodes.Status400BadRequest,
                            Message = "请求JSON格式错误",
                            Details = "JSON语法不符合规范,请检查后重试"
                        };
                        
                        // 重置响应并写入自定义内容
                        context.Response.ContentLength = null;
                        context.Response.ContentType = "application/json";
                        await context.Response.WriteAsJsonAsync(errorResponse);
                    }
                }
            });
            
            next(app);
        };
    }
}
  • 在Program.cs中注册:
builder.Services.AddTransient<IStartupFilter, ErrorResponseStartupFilter>();

3. 禁用默认模型绑定错误响应(补充方案)

关闭框架自动生成的模型绑定错误,再通过全局ActionFilter处理,但对JSON解析错误的覆盖性不如前两种:

builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.SuppressModelStateInvalidFilter = true;
});

添加全局ActionFilter检查模型状态,针对性处理空键的模型错误(JSON解析错误的特征),返回自定义响应。

关键注意事项

  • 确保CustomErrorResponse类的结构与OpenAPI规范定义的错误响应完全一致,保证NSwag生成的文档匹配。
  • 测试时需发送确有语法错误的JSON(比如缺失引号、逗号错误),验证响应格式是否符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 11:23:25