如何自定义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
相关产品推荐
相关产品推荐

