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

如何对带有Action Filter参数验证的MVC控制器进行单元测试?

我之前在项目里实现过一模一样的场景,用Action Filter结合FluentValidation自动验证Web API的参数,下面是完整的实现方案:

完整实现步骤

1. 自定义FluentValidation Action Filter

这个Filter会在Action执行前自动找到参数对应的验证器,完成验证逻辑:

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

public class FluentValidationActionFilter : IActionFilter
{
    private readonly IServiceProvider _serviceProvider;

    // 通过构造注入获取服务容器,用于解析验证器
    public FluentValidationActionFilter(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        // 遍历控制器动作的所有参数
        foreach (var parameter in context.ActionDescriptor.Parameters)
        {
            var paramType = parameter.ParameterType;
            // 构建对应参数类型的IValidator<T>接口类型
            var validatorType = typeof(IValidator<>).MakeGenericType(paramType);
            // 从服务容器中获取验证器实例
            var validator = _serviceProvider.GetService(validatorType) as IValidator;

            if (validator != null && context.ActionArguments.TryGetValue(parameter.Name, out var paramValue))
            {
                // 执行参数验证
                var validationResult = validator.Validate(new ValidationContext<object>(paramValue));
                
                if (!validationResult.IsValid)
                {
                    // 将验证错误映射到ModelState
                    foreach (var error in validationResult.Errors)
                    {
                        context.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
                    }
                    
                    // 验证失败时直接返回400 Bad Request,终止后续动作执行
                    context.Result = new Microsoft.AspNetCore.Mvc.BadRequestObjectResult(context.ModelState);
                    return;
                }
            }
        }
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // 动作执行后无需额外操作,留空即可
    }
}

2. 注册服务与Filter

在Program.cs中配置FluentValidation和自定义Filter,让框架能自动发现验证器并应用Filter:

var builder = WebApplication.CreateBuilder(args);

// 添加控制器服务
builder.Services.AddControllers();

// 注册FluentValidation服务,自动发现指定程序集中的验证器
builder.Services.AddFluentValidationAutoValidation()
                .AddFluentValidationClientsideAdapters()
                // 替换成你的验证器所在的程序集(比如包含Program的程序集,或者单独的验证器程序集)
                .AddValidatorsFromAssemblyContaining<Program>();

// 注册自定义Action Filter为Scoped服务
builder.Services.AddScoped<FluentValidationActionFilter>();

var app = builder.Build();

// 中间件配置
app.UseHttpsRedirection();
app.UseAuthorization();

// 全局为所有控制器动作添加Filter
app.MapControllers().AddFilter<FluentValidationActionFilter>();

app.Run();

3. 创建实体类与对应的验证器

定义你的业务实体,以及继承自AbstractValidator<T>的验证器类:

// 示例实体类
public class UserCreateModel
{
    public string Username { get; set; }
    public string Email { get; set; }
    public int Age { get; set; }
}

// 对应的FluentValidation验证器
public class UserCreateModelValidator : AbstractValidator<UserCreateModel>
{
    public UserCreateModelValidator()
    {
        RuleFor(x => x.Username)
            .NotEmpty().WithMessage("用户名不能为空")
            .MinimumLength(3).WithMessage("用户名长度不能少于3个字符");

        RuleFor(x => x.Email)
            .NotEmpty().WithMessage("邮箱地址不能为空")
            .EmailAddress().WithMessage("请输入有效的邮箱格式");

        RuleFor(x => x.Age)
            .GreaterThanOrEqualTo(18).WithMessage("年龄必须年满18周岁");
    }
}

4. 控制器使用示例

在控制器动作中直接使用实体参数即可,Filter会自动完成验证:

[ApiController]
[Route("api/users")]
public class UsersController : ControllerBase
{
    [HttpPost]
    public async Task<IActionResult> CreateUser([FromBody] UserCreateModel model)
    {
        // 验证不通过的话,Filter已经提前返回BadRequest了,这里直接写业务逻辑
        // 比如保存用户到数据库等操作
        return Ok(new { Message = "用户创建成功", User = model });
    }
}

一些实用注意点

  • 嵌套对象验证:如果你的实体包含嵌套对象,只要为嵌套对象也定义对应的AbstractValidator<T>,FluentValidation会自动递归验证嵌套属性。
  • 局部应用Filter:如果不想全局应用Filter,可以在特定控制器或动作上添加[ServiceFilter(typeof(FluentValidationActionFilter))]特性来启用验证。
  • 验证器可见性:确保你的验证器类是public的,否则FluentValidation无法自动发现它们。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:26:55