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

ASP.NET Core MVC中如何使用FluentValidation验证CQRS查询并在验证失败时跳转404错误页

解决方案:ASP.NET Core MVC中CQRS查询的FluentValidation验证与错误重定向

我来帮你一步步解决这两个问题,这在结合CQRS和FluentValidation的ASP.NET Core项目里是很典型的场景。


问题1:使用FluentValidation在CQRS查询执行前验证参数

要实现查询参数的前置验证,我们可以借助FluentValidation的验证器,配合MediatR的管道行为来实现自动化验证,这样能让Handler只专注于业务逻辑,不用关心参数校验。

步骤1:安装必要的NuGet包

首先确保你已经安装了以下包:

Install-Package FluentValidation
Install-Package FluentValidation.AspNetCore

步骤2:为查询创建验证器

为GetTagByIdQuery编写对应的验证器类,定义参数规则(比如ID必须大于0):

using FluentValidation;

public class GetTagByIdQueryValidator : AbstractValidator<GetTagByIdQuery>
{
    public GetTagByIdQueryValidator()
    {
        // 规则:ID必须大于0,否则抛出验证错误
        RuleFor(query => query.Id)
            .GreaterThan(0)
            .WithMessage("标签ID必须是有效的正整数");
    }
}

步骤3:添加MediatR验证管道行为

创建一个管道行为类,让MediatR在执行Handler之前自动触发验证:

using FluentValidation;
using MediatR;

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
    {
        _validators = validators;
    }

    public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
    {
        // 执行所有针对当前请求的验证
        if (_validators.Any())
        {
            var validationContext = new ValidationContext<TRequest>(request);
            var validationResults = await Task.WhenAll(
                _validators.Select(validator => validator.ValidateAsync(validationContext, cancellationToken))
            );

            // 收集所有验证失败的错误
            var validationFailures = validationResults
                .SelectMany(result => result.Errors)
                .Where(error => error != null)
                .ToList();

            if (validationFailures.Any())
            {
                throw new ValidationException(validationFailures);
            }
        }

        // 验证通过,继续执行Handler
        return await next();
    }
}

步骤4:注册服务到DI容器

在Program.cs中注册验证器和管道行为,让框架能自动发现和使用它们:

// 注册当前程序集中所有的FluentValidation验证器
builder.Services.AddValidatorsFromAssemblyContaining<GetTagByIdQueryValidator>();

// 注册MediatR的验证管道行为
builder.Services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));

这样一来,当你调用_mediator.Send(new GetTagByIdQuery { Id = id })时,系统会先自动执行验证,如果ID不符合规则(比如小于等于0),就会抛出ValidationException,不会进入Handler执行查询。


问题2:验证失败/查询结果为空时重定向到404页面

这里分两种场景:参数验证失败和查询结果为空,我们可以通过全局异常处理或者Action内捕获的方式来实现重定向。

方案1:全局异常过滤器(推荐)

创建一个全局异常过滤器,统一处理验证异常和资源未找到的情况:

第一步:定义自定义NotFoundException(可选)

为了区分"资源不存在"的场景,我们可以自定义一个异常类:

public class NotFoundException : Exception
{
    public NotFoundException(string message) : base(message) { }
}

第二步:修改Query Handler,资源不存在时抛异常

修改GetTagByIdQueryHandler,当查询不到标签时抛出自定义的NotFoundException:

public async Task<Tag> Handle(GetTagByIdQuery request, CancellationToken cancellationToken)
{
    var tag = await _unitOfWork.Tags.GetAsync(request.Id);
    if (tag == null)
    {
        throw new NotFoundException($"ID为{request.Id}的标签不存在");
    }
    return tag;
}

第三步:创建全局异常过滤器

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

public class CustomExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        // 处理验证异常或资源未找到异常
        if (context.Exception is ValidationException || context.Exception is NotFoundException)
        {
            // 重定向到自定义的404页面(假设你有Error控制器的NotFound方法)
            context.Result = new RedirectToActionResult("NotFound", "Error", null);
            context.ExceptionHandled = true;
        }
    }
}

第四步:注册过滤器到MVC

在Program.cs中添加这个过滤器:

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<CustomExceptionFilter>();
});

方案2:在Action内手动捕获异常

如果你不想用全局过滤器,也可以在Action里直接捕获异常并处理:

public async Task<IActionResult> Edit(int id)
{
    try
    {
        var tag = await _mediator.Send(new GetTagByIdQuery { Id = id });
        return View(tag);
    }
    catch (ValidationException)
    {
        // 参数验证失败,重定向到404
        return RedirectToAction("NotFound", "Error");
    }
    catch (NotFoundException)
    {
        // 资源不存在,返回内置的NotFound结果或重定向
        return RedirectToAction("NotFound", "Error");
        // 或者直接返回内置404:return NotFound();
    }
}

内容的提问来源于stack exchange,提问作者AmRo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 22:07:34