Mediatr 行为管道中的验证

Posted

技术标签:

【中文标题】Mediatr 行为管道中的验证【英文标题】:Validation in a Mediatr behaviour pipeline 【发布时间】:2018-01-18 12:25:52 【问题描述】:

我在我的 web api 2 项目中使用 Mediatr 4。连同 FluentValidation 和 Unity,我一直在添加管道行为来验证我的请求。

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 Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    
        var context = new ValidationContext(request);
        var failures = _validators
            .Select(v => v.Validate(context))
            .SelectMany(result => result.Errors)
            .Where(f => f != null)
            .ToList();

        if (failures.Count != 0)
        
            throw new ValidationException(failures);
        
        return next();
    

这一切都很好,但我真的希望能够在一个完整的响应中返回验证。我正在努力做出这样的改变,要么让它编译,要么没有 Unity throw runtime 解决问题。

我想有类似的东西:

public class CommandResult : IResponseBase

    private List<ValidationFailure> _validationFailures = new List<ValidationFailure>();
    private readonly string _correlationid;

    public CommandResult(string correlationid)
    
        _correlationid = correlationid;
    
    public bool IsSuccess => _validationFailures.Count == 0;

    public static implicit operator bool(CommandResult result)
    
        return result.IsSuccess;
    

    public void AddFailures(List<ValidationFailure> results)
    
        _validationFailures = results;
    

    public List<ValidationFailure> Failures => _validationFailures;

    public string CorrelationId => _correlationid;

在此基础上,我在行为中添加了一个约束:

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
    where TResponse : IResponseBase, new()

但尝试返回 CommandResult 而不是抛出异常会给我带来类型转换问题,而且感觉我把它弄得太复杂了,而且我错过了一些非常基本的东西。

【问题讨论】:

这种方法的验证规则在哪里? @Alexsandro_xpt 如果您查看 FluentValidation (fluentvalidation.net/start) 的文档,您可以在外部类中定义规则。然后使用您首选的 DI 容器将这些注入到验证行为中。 【参考方案1】:

两种类型的验证(至少

    请求数据验证 - 验证接收到的 HTTP 请求包含正确格式的所需数据。前任。 电子邮件字段是必需的,并且应该是有效的电子邮件地址**

    特定于域的验证 - 验证 Command 尝试应用于当前应用程序状态的修改在 域预期 中是否有效。前任。 电子邮件 必须是唯一的。 电子邮件不应被列入黑名单

第一种类型的验证进入ActionFilter

第二种验证进入IPipelineBehavior

Domain Command Patterns - Validation

【讨论】:

虽然我原则上同意,但由于域验证器无法信任客户端,您最终不会在域验证器中复制请求验证吗? @Cocowalla “客户端”是指 Controller.Action 创建命令并将其发送到域,或者说浏览器发送请求?【参考方案2】:

让我为您的问题提出另一种方法。代替管道使用自定义 ActionFilterAttribute 在您的请求到达控制器之前执行验证,并且必须由 mediatr 路由。以下示例使用 Autofac 作为容器,但我希望您能理解并能够适当地修改代码。作为奖励 - 您的 Mediatr 请求或处理程序不需要更改。验证将在调用控制器操作之前执行,并且在您收到有效请求之前不会进一步执行。

public class ValidateModelStateFilter : ActionFilterAttribute, IAutofacActionFilter

    private readonly IValidatorFactory _factory;

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="factory"></param>
    public ValidateModelStateFilter(IValidatorFactory factory)
    
        _factory = factory;
    

    /// <summary>
    /// 
    /// </summary>
    /// <param name="actionContext"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    
        base.OnActionExecutingAsync(actionContext, cancellationToken);

        IEnumerable<object> parameters = actionContext.ActionArguments.Select(x => x.Value).Where(x => x != null);
        foreach (var parameter in parameters)
        
            Type argumentType = parameter.GetType();
            if (argumentType == typeof(int) || argumentType == typeof(string))
            
                continue;
            
            IValidator validator = _factory.GetValidator(argumentType);
            if (validator != null)
            
                ValidationResult validationResult = validator.Validate(parameter);
                if (!validationResult.IsValid)
                

                   // place your formatting logic here. 
                    actionContext.Response = <your formatted response>;
                
            
        

        return Task.FromResult(0);
    


【讨论】:

MediatR 之类的东西的全部意义在于引入与命令/查询绑定的横切关注点,因此它将由 MediatR 管道处理,并且所有内容都包含在该管道中,紧凑单元。通过使用 OnActionExecutingAsync,您可以让事情变得不那么明确,更加分散在代码库中并且更难管理。

以上是关于Mediatr 行为管道中的验证的主要内容,如果未能解决你的问题,请参考以下文章

向 MediatR 行为管道添加验证?

来自管道行为的 MediatR 流畅的验证响应

使用mediatR管道行为添加验证。

返回带有错误的响应,而不是在验证管道 mediatr 3 中抛出异常

Mediatr:单元测试行为/验证

如何为 ASP.NET Core 注册和使用 MediatR 管道处理程序?