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

.NET 7中如何向自定义ValidationFilter传递FluentValidation的RuleSets

问题背景

自.NET 6发布起,我便开始使用Minimal API,此前采用手动方式实现验证,代码如下:

app.MapPost("api/user", async ([FromService] IValidator<UserDto> validator, [FromBody] UserDto user) => 
{
   var validationResult = await validator.ValidateAsync(user);

   if (!validationResult.IsValid)
   {
      return Results.BadRequest(string.Join("/n", validationResult.Errors));
   }
  
  ...
})

.NET 7新增Filters特性后,我实现了自定义验证过滤器,代码如下:

public class ValidationFilter<T> : IEndpointFilter where T : class
{
 private readonly IValidator<T> _validator;

 public ValidationFilter(IValidator<T> validator)
 {
    _validator = validator;
 }

 public async ValueTask<object> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
 {
    var obj = context.Arguments.FirstOrDefault(x => x?.GetType() == typeof(T)) as T;

    if (obj is null)
    {
        return Results.BadRequest();
    }
    
    var validationResult = await _validator.ValidateAsync(obj);

    if (!validationResult.IsValid)
    {
        return Results.BadRequest(string.Join("/n", validationResult.Errors));
    }

    return await next(context);
  }
}

我通过调用AddEndPointFilter<T>()来使用该过滤器,示例如下:

app.MapPost("api/user", (..) => { ... }).AddEndPointFilter<ValidationFilter>();

该实现运行正常,但我的FluentValidation中定义了RuleSet(),需在PUT请求中使用,请问如何将这些RuleSets传递给自定义的ValidationFilter?

解决方案

要在自定义过滤器中支持FluentValidation的RuleSet,可通过以下几种方式实现:

1. 修改过滤器类,构造注入RuleSet参数

调整ValidationFilter<T>,让它接受RuleSet数组作为构造参数,在验证时传入指定规则集:

public class ValidationFilter<T> : IEndpointFilter where T : class
{
 private readonly IValidator<T> _validator;
 private readonly string[] _ruleSets;

 public ValidationFilter(IValidator<T> validator, string[] ruleSets)
 {
    _validator = validator;
    _ruleSets = ruleSets;
 }

 public async ValueTask<object> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
 {
    var obj = context.Arguments.FirstOrDefault(x => x?.GetType() == typeof(T)) as T;

    if (obj is null)
    {
        return Results.BadRequest();
    }
    
    // 传入指定RuleSet执行验证
    var validationResult = await _validator.ValidateAsync(obj, options => options.IncludeRuleSets(_ruleSets));

    if (!validationResult.IsValid)
    {
        return Results.BadRequest(string.Join("/n", validationResult.Errors.Select(e => e.ErrorMessage)));
    }

    return await next(context);
  }
}

使用时通过AddEndpointFilterFactory传递RuleSet:

app.MapPut("api/user/{id}", (UserDto user) => { ... })
   .AddEndpointFilterFactory((context, next) =>
   {
       var validator = context.ServiceProvider.GetRequiredService<IValidator<UserDto>>();
       // 指定要使用的RuleSet,比如"Update"
       var filter = new ValidationFilter<UserDto>(validator, new[] { "Update" });
       return filter.InvokeAsync;
   });

2. 封装扩展方法简化调用

为避免重复编写工厂代码,封装一个扩展方法:

public static class EndpointFilterExtensions
{
    public static RouteHandlerBuilder AddValidationFilter<T>(this RouteHandlerBuilder builder, params string[] ruleSets) where T : class
    {
        return builder.AddEndpointFilterFactory((context, next) =>
        {
            var validator = context.ServiceProvider.GetRequiredService<IValidator<T>>();
            var filter = new ValidationFilter<T>(validator, ruleSets);
            return filter.InvokeAsync;
        });
    }
}

使用时更简洁:

// POST请求不指定RuleSet,使用默认规则
app.MapPost("api/user", (UserDto user) => { ... })
   .AddValidationFilter<UserDto>();

// PUT请求指定"Update" RuleSet
app.MapPut("api/user/{id}", (UserDto user) => { ... })
   .AddValidationFilter<UserDto>("Update");

3. 通过端点元数据动态获取RuleSet

如果希望用特性标记指定RuleSet,先定义自定义特性:

[AttributeUsage(AttributeTargets.Method)]
public class ValidationRuleSetAttribute : Attribute
{
    public string[] RuleSets { get; }

    public ValidationRuleSetAttribute(params string[] ruleSets)
    {
        RuleSets = ruleSets;
    }
}

修改过滤器,从端点元数据中读取特性信息:

public async ValueTask<object> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
    var obj = context.Arguments.FirstOrDefault(x => x?.GetType() == typeof(T)) as T;

    if (obj is null)
    {
        return Results.BadRequest();
    }

    // 从端点元数据中获取RuleSet配置
    var ruleSetAttr = context.HttpContext.GetEndpoint()?.Metadata.GetMetadata<ValidationRuleSetAttribute>();
    var ruleSets = ruleSetAttr?.RuleSets ?? Array.Empty<string>();
    
    var validationResult = await _validator.ValidateAsync(obj, options => 
    {
        if (ruleSets.Any())
        {
            options.IncludeRuleSets(ruleSets);
        }
    });

    if (!validationResult.IsValid)
    {
        return Results.BadRequest(string.Join("/n", validationResult.Errors.Select(e => e.ErrorMessage)));
    }

    return await next(context);
}

使用时给端点标记特性即可:

app.MapPut("api/user/{id}", (UserDto user) => { ... })
   .AddEndpointFilter<ValidationFilter<UserDto>>()
   .WithMetadata(new ValidationRuleSetAttribute("Update"));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 17:40:45