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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 06:25:14