ASP.NET Core中IExceptionHandler未处理特性抛出的ValidationException问题
我定义了如下DTO并使用数据注解做验证:
public record CreatorRequestTo( [property: JsonPropertyName("id")] long Id, [property: JsonPropertyName("login")] [StringLength(64, MinimumLength = 2)] string Login, [property: JsonPropertyName("password")] [StringLength(128, MinimumLength = 8)] string Password, [property: JsonPropertyName("firstname")] [StringLength(64, MinimumLength = 2)] string FirstName, [property: JsonPropertyName("lastname")] [StringLength(64, MinimumLength = 2)] string LastName );
发送包含无效数据(比如密码长度小于8位)的POST请求时,预期会触发ValidationException并被自定义全局异常处理器处理,但实际返回的是默认的ProblemDetails响应:
{ "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1", "title": "One or more validation errors occurred.", "status": 400, "errors": { "Password": [ "The field Password must be a string with a minimum length of 8 and a maximum length of 128." ] }, "traceId": "00-af83651d89556659fef605e3689da98f-cffd2070a3d9bc8f-00" }
自定义全局异常处理器代码如下:
public class GlobalExceptionHandler : IExceptionHandler { public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { var errorCode = exception switch { EntityNotFoundException => (int)HttpStatusCode.BadRequest + "01", ValidationException => (int)HttpStatusCode.BadRequest + "02", _ => (int)HttpStatusCode.InternalServerError + "00" }; var response = new ErrorResponseTo(exception.Message, errorCode); await httpContext.Response.WriteAsJsonAsync(response, cancellationToken); return true; } }
Program.cs配置:
... builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); builder.Services.AddProblemDetails(); ... var app = builder.Build(); app.UseExceptionHandler();
我希望返回自定义格式的错误响应:
{ "errorMessage": "<exception message>", "errorCode": 40002 }
请问为什么ValidationException未被自定义处理器捕获?
ASP.NET Core中,带有[ApiController]特性的控制器会启用自动模型验证机制:在请求进入Action方法之前,框架会自动校验模型状态,一旦验证失败,会直接返回默认的ProblemDetails响应,不会抛出ValidationException。因此你的全局异常处理器根本没有机会捕获到这个异常。
另外,原处理器中的errorCode存在类型问题:将int类型的状态码与字符串拼接,会生成字符串类型的错误码(比如400+"02"得到"40002"),但你期望的响应中errorCode是数字类型,这会导致序列化后的响应不符合预期。
1. 禁用自动模型验证的默认响应
在Program.cs中配置ApiBehaviorOptions,关闭自动返回ProblemDetails的过滤器,让模型验证失败时抛出ValidationException:
builder.Services.AddControllers() .ConfigureApiBehaviorOptions(options => { // 禁用自动返回ProblemDetails,触发ValidationException options.SuppressModelStateInvalidFilter = true; }); builder.Services.AddExceptionHandler<GlobalExceptionHandler>(); // 若不需要默认ProblemDetails功能,可移除AddProblemDetails() // builder.Services.AddProblemDetails(); var app = builder.Build(); app.UseExceptionHandler();
2. 修正全局异常处理器的错误码逻辑
调整错误码的生成方式,确保返回数字类型的错误码,并设置正确的HTTP状态码:
public class GlobalExceptionHandler : IExceptionHandler { public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { var (statusCode, errorCode, errorMessage) = exception switch { EntityNotFoundException => (HttpStatusCode.BadRequest, 40001, exception.Message), ValidationException validationEx => (HttpStatusCode.BadRequest, 40002, validationEx.Message), _ => (HttpStatusCode.InternalServerError, 50000, "服务器内部错误") }; // 设置HTTP响应状态码 httpContext.Response.StatusCode = (int)statusCode; var response = new ErrorResponseTo(errorMessage, errorCode); await httpContext.Response.WriteAsJsonAsync(response, cancellationToken); return true; } } // 确保ErrorResponseTo的属性名称和类型匹配预期响应 public record ErrorResponseTo(string ErrorMessage, int ErrorCode);
3. 验证效果
修改完成后,发送无效数据的POST请求时,框架会抛出ValidationException,全局处理器捕获后会返回你期望的自定义格式响应:
{ "errorMessage": "One or more validation errors occurred.", "errorCode": 40002 }
内容的提问来源于stack exchange,提问作者Kristie _

