在 Mediatr 行为管道中进行验证

Dev*_*low 5 c# unity-container fluentvalidation mediatr

我在我的 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这一切都很好,但我真的希望能够在打包的响应中返回验证。我正在努力进行这样的更改,要么让它编译,要么没有 Unity 抛出运行时解决问题。

我想有这样的事情:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
    where TResponse : IResponseBase, new()
Run Code Online (Sandbox Code Playgroud)

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

Dra*_*fly 0

让我为您的问题建议另一种方法。在请求命中控制器之前,使用自定义 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);
    }

}
Run Code Online (Sandbox Code Playgroud)

}