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

如何自定义ASP.NET Core默认Bad Request错误响应格式?

捕获并格式化ASP.NET Core的Bad Request响应

针对你遇到的两种Bad Request场景(查询参数验证失败、无效JSON请求体),因为这些响应是框架直接生成的而非抛出异常,所以ExceptionFilter无法捕获,需要用不同方案处理:

一、处理模型验证错误(如page参数无效)

这类错误是框架在模型绑定/验证阶段生成的ValidationProblemResult,可以通过以下两种方式处理:

1. 自定义ProblemDetailsFactory(推荐)

这是ASP.NET Core官方提供的扩展点,能统一替换所有框架生成的ProblemDetails格式,包括验证错误、404等其他错误。

步骤:

  • 实现自定义ProblemDetailsFactory:
public class CustomProblemDetailsFactory : ProblemDetailsFactory
{
    public override ProblemDetails CreateProblemDetails(HttpContext httpContext, int? statusCode = null, string? title = null, string? type = null, string? detail = null, string? instance = null)
    {
        var problem = base.CreateProblemDetails(httpContext, statusCode, title, type, detail, instance);
        // 移除不需要的字段,添加自定义内容
        problem.Extensions.Remove("traceId");
        problem.Extensions.Add("custom_code", "VALIDATION_FAILED");
        return problem;
    }

    public override ValidationProblemDetails CreateValidationProblemDetails(HttpContext httpContext, ModelStateDictionary modelStateDictionary, int? statusCode = null, string? title = null, string? type = null, string? detail = null, string? instance = null)
    {
        var validationProblem = base.CreateValidationProblemDetails(httpContext, modelStateDictionary, statusCode, title, type, detail, instance);
        // 自定义验证错误的结构
        var customErrors = modelStateDictionary.ToDictionary(
            kv => kv.Key,
            kv => kv.Value.Errors.Select(e => e.ErrorMessage).ToArray()
        );
        validationProblem.Errors.Clear();
        validationProblem.Errors.AddRange(customErrors);
        
        // 替换默认信息,移除冗余字段
        validationProblem.Title = "请求参数验证失败";
        validationProblem.Extensions.Remove("traceId");
        validationProblem.Extensions.Add("custom_message", "请检查输入参数是否合法");
        return validationProblem;
    }
}
  • 在DI容器中注册:
builder.Services.AddSingleton<ProblemDetailsFactory, CustomProblemDetailsFactory>();

2. 使用IAlwaysRunResultFilter

通过ResultFilter拦截ValidationProblemResult,替换为自定义响应格式:

步骤:

  • 实现过滤器:
public class CustomValidationResultFilter : IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is ValidationProblemResult validationResult)
        {
            // 构建自定义错误对象
            var customError = new
            {
                code = "400",
                message = "请求参数无效",
                errors = validationResult.ModelState.ToDictionary(
                    kv => kv.Key,
                    kv => kv.Value.Errors.Select(e => e.ErrorMessage).ToArray()
                )
            };

            // 替换原始响应
            context.Result = new JsonResult(customError)
            {
                StatusCode = StatusCodes.Status400BadRequest
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // 无需额外处理
    }
}
  • 注册过滤器到Controllers:
builder.Services.AddControllers(options =>
{
    options.Filters.Add<CustomValidationResultFilter>();
});

二、处理无效JSON请求体

这类错误发生在请求体解析阶段,还未进入模型绑定流程,需要用Middleware拦截响应:

步骤:

  • 实现中间件:
public class InvalidJsonHandlingMiddleware
{
    private readonly RequestDelegate _next;

    public InvalidJsonHandlingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // 保存原始响应流,用于后续替换
        var originalResponseBody = context.Response.Body;
        using var newResponseBody = new MemoryStream();
        context.Response.Body = newResponseBody;

        try
        {
            await _next(context);

            // 检查是否是400错误,且为无效JSON导致
            if (context.Response.StatusCode == StatusCodes.Status400BadRequest)
            {
                newResponseBody.Seek(0, SeekOrigin.Begin);
                var responseContent = await new StreamReader(newResponseBody).ReadToEndAsync();

                // 根据响应内容判断是否是无效JSON错误
                if (responseContent.Contains("Invalid JSON") || responseContent.Contains("One or more validation errors occurred."))
                {
                    // 构建自定义错误格式
                    var customError = new
                    {
                        code = "400",
                        message = "请求体格式错误,请提供合法的JSON数据",
                        errors = new[] { "无效的JSON格式" }
                    };

                    // 重置响应,输出自定义内容
                    context.Response.Body = originalResponseBody;
                    context.Response.ContentType = "application/json";
                    await context.Response.WriteAsJsonAsync(customError);
                }
                else
                {
                    // 非无效JSON错误,还原原始响应
                    newResponseBody.Seek(0, SeekOrigin.Begin);
                    await newResponseBody.CopyToAsync(originalResponseBody);
                }
            }
            else
            {
                // 其他状态码,还原原始响应
                newResponseBody.Seek(0, SeekOrigin.Begin);
                await newResponseBody.CopyToAsync(originalResponseBody);
            }
        }
        finally
        {
            context.Response.Body = originalResponseBody;
        }
    }
}
  • 注册中间件(注意要放在UseRouting之前):
app.UseMiddleware<InvalidJsonHandlingMiddleware>();
app.UseRouting();

// 其他中间件配置...
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

总结

  • 模型验证类Bad Request:优先用ProblemDetailsFactory,统一且符合官方设计;也可选择IAlwaysRunResultFilter。
  • 无效JSON类Bad Request:必须用Middleware拦截,因为错误发生在管道早期,Filter无法捕获。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 22:50:37