ASP.NET Core异常中间件无法将Exception转为ValidationException求助
问题:ASP.NET Core异常处理中间件无法捕获FluentValidation的ValidationException
我在ASP.NET Core的异常处理中间件中遇到问题:使用Fluent Validation结合MediatR管道验证用户输入,验证不通过时抛出ValidationException:
if (errors.Any()) { throw new ValidationException(errors); }
该异常会被异常处理中间件捕获,但无法强制转换为ValidationException,也就无法获取验证错误信息:
public async Task InvokeAsync(HttpContext context) { try { await _next(context); } catch (Exception e) { await HandleExceptionAsync(context, e); } }
若直接捕获ValidationException则无法触发捕获逻辑;强制转换时会抛出以下错误:
System.InvalidCastException: Unable to cast object of type 'System.AggregateException' to type 'FluentValidation.ValidationException'. at Application.Middlewares.ExceptionHandlingMiddleware.Invoke(HttpContext context) in C:\Programs\X\Application\Middleware\ExceptionHandlingMiddleware.cs:line 24 at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
我的依赖注入配置如下:
public static IServiceCollection AddApplication(this IServiceCollection services) { services.AddTransient<IPasswordHasher, PasswordHasher>(); services.AddAutoMapper(Assembly.GetExecutingAssembly()); services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); services.AddMediatR(c => c.RegisterServicesFromAssembly(Assembly.GetExecutingAssembly())); services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>)); return services; }
所有服务均已注册,验证管道和异常处理器能正常工作,但无法将捕获的异常转换为ValidationException,请问该如何正确处理?
解决方案
1. 在中间件中解析AggregateException,提取底层ValidationException
异常被包装为AggregateException是核心问题,需要在catch逻辑中展开异常,获取真正的ValidationException:
public async Task InvokeAsync(HttpContext context) { try { await _next(context); } catch (Exception e) { // 递归展开异常,获取最底层的真实异常 var targetException = UnwrapException(e); if (targetException is ValidationException validationException) { // 专门处理验证异常,返回结构化错误响应 context.Response.StatusCode = StatusCodes.Status400BadRequest; context.Response.ContentType = "application/json"; var errorResponse = new { StatusCode = 400, Message = "输入验证失败", Errors = validationException.Errors.Select(err => new { err.PropertyName, err.ErrorMessage }) }; await context.Response.WriteAsJsonAsync(errorResponse); } else { // 处理其他类型异常 await HandleExceptionAsync(context, targetException); } } } // 递归展开AggregateException private Exception UnwrapException(Exception ex) { if (ex is AggregateException aggEx && aggEx.InnerExceptions.Count == 1) { return UnwrapException(aggEx.InnerException); } return ex; }
2. 确保MediatR验证管道的异步实现正确
检查你的ValidationBehaviour,确保所有异步操作使用await,避免使用.Wait()或.Result导致异常被包装:
public class ValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse> { private readonly IEnumerable<IValidator<TRequest>> _validators; public ValidationBehaviour(IEnumerable<IValidator<TRequest>> validators) { _validators = validators; } public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken) { var validationContext = new ValidationContext<TRequest>(request); // 异步执行所有验证 var validationResults = await Task.WhenAll( _validators.Select(validator => validator.ValidateAsync(validationContext, cancellationToken)) ); var errors = validationResults .SelectMany(result => result.Errors) .Where(error => error != null) .ToList(); if (errors.Any()) { throw new ValidationException(errors); } return await next(); } }
3. 可选:直接分层捕获异常
也可以通过多catch块分别处理不同类型的异常,逻辑更直观:
public async Task InvokeAsync(HttpContext context) { try { await _next(context); } catch (ValidationException validationEx) { // 直接捕获未被包装的验证异常 await HandleValidationExceptionAsync(context, validationEx); } catch (AggregateException aggEx) { // 从AggregateException中提取ValidationException var validationEx = aggEx.InnerExceptions.FirstOrDefault(ex => ex is ValidationException) as ValidationException; if (validationEx != null) { await HandleValidationExceptionAsync(context, validationEx); } else { await HandleExceptionAsync(context, aggEx); } } catch (Exception e) { // 处理其他所有异常 await HandleExceptionAsync(context, e); } }
内容的提问来源于stack exchange,提问作者RidingBoi
相关产品推荐
相关产品推荐

