.NET Core 7模型验证异常无法被Exception Handler捕获的解决咨询
解决.NET Core 7中模型验证异常无法流入统一异常处理器的问题
问题根源
ASP.NET Core的[ApiController]属性默认会自动处理模型验证失败的情况,直接返回400响应,不会抛出异常,因此你的UseExceptionHandler中间件无法捕获到这类错误。同时,若直接在InvalidModelStateResponseFactory中抛出异常,该工厂方法的执行逻辑不会触发异常处理中间件,而是会被框架直接捕获并返回默认错误。
解决方案一:让模型验证失败抛出异常,复用现有异常处理器
这个方案通过禁用默认模型验证行为,自定义过滤器抛出验证异常,让现有ErrorController统一处理。
- 禁用默认模型验证过滤器
在Program.cs中配置ApiBehaviorOptions,关闭自动处理模型验证的功能:
builder.Services.Configure<ApiBehaviorOptions>(options => { options.SuppressModelStateInvalidFilter = true; });
- 实现自定义验证过滤器
创建一个ActionFilter,在模型验证失败时抛出包含详细错误的自定义异常:
public class CustomValidationFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { if (!context.ModelState.IsValid) { var validationErrors = context.ModelState .Where(entry => entry.Value.Errors.Any()) .ToDictionary( entry => entry.Key, entry => entry.Value.Errors.Select(error => error.ErrorMessage).ToArray() ); throw new CustomValidationException("模型验证失败", validationErrors); } } public void OnActionExecuted(ActionExecutedContext context) { } } // 自定义验证异常类 public class CustomValidationException : Exception { public Dictionary<string, string[]> Errors { get; } public CustomValidationException(string message, Dictionary<string, string[]> errors) : base(message) { Errors = errors; } }
- 注册自定义过滤器
在Program.cs的AddControllers中添加这个过滤器:
builder.Services.AddControllers(options => { options.Filters.Add<CustomValidationFilter>(); });
- 在ErrorController中处理自定义验证异常
修改ErrorController的错误处理方法,加入对CustomValidationException的处理:
// DevError方法 [Route("dev-error")] public IAttempt DevError() { var context = HttpContext.Features.Get<IExceptionHandlerFeature>(); var exception = context.Error; return exception switch { CustomValidationException validationEx => Attempt.Fail(validationEx), _ => Attempt.Fail(exception) }; } // Error方法 [Route("error")] public IAttempt Error() { var context = HttpContext.Features.Get<IExceptionHandlerFeature>(); var exception = context.Error; _logger.Log(LogLevel.Error, exception, exception.Message); return exception switch { UnauthorizedAccessException => { Response.StatusCode = (int)HttpStatusCode.Unauthorized; return Attempt.Fail("Unauthorised"); }, CustomValidationException validationEx => { Response.StatusCode = (int)HttpStatusCode.BadRequest; return Attempt.Fail(validationEx.Errors); }, _ => { Response.StatusCode = (int)HttpStatusCode.InternalServerError; return Attempt.Fail("Generic Error"); } }; }
解决方案二:重写InvalidModelStateResponseFactory,统一返回IAttempt格式
如果不想抛出异常,可以直接修改模型验证失败的响应格式,使其返回和异常处理器一致的IAttempt结构,实现统一格式输出。
在Program.cs中配置InvalidModelStateResponseFactory:
builder.Services.AddControllers(options => { options.InvalidModelStateResponseFactory = context => { var validationErrors = context.ModelState .Where(entry => entry.Value.Errors.Any()) .ToDictionary( entry => entry.Key, entry => entry.Value.Errors.Select(error => error.ErrorMessage).ToArray() ); var failedAttempt = Attempt.Fail(validationErrors); return new JsonResult(failedAttempt) { StatusCode = (int)HttpStatusCode.BadRequest }; }; });
这种方式不需要修改现有异常处理器,模型验证失败会直接返回符合要求的IAttempt响应,同时保留原有异常处理器处理其他异常的逻辑。
内容的提问来源于stack exchange,提问作者Greg
相关产品推荐
相关产品推荐

