Mediatr 行为管道中的验证
Validation in a Mediatr behaviour pipeline
我在我的 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();
}
}
一切正常,但我真的很希望能够 return 在结束响应中进行验证。我正在努力进行这样的更改,要么编译它,要么不让 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()
但是尝试 return 一个 CommandResult 而不是抛出异常给我带来了类型转换问题,感觉我把它弄得太复杂了,我遗漏了一些非常基本的东西。
让我为您的问题建议另一种方法。在您的请求命中控制器并且必须由 mediatr 路由之前,使用自定义 ActionFilterAttribute 而不是管道来执行验证。以下示例使用 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);
}
}
}
有两种种验证(至少)
请求数据验证 - 验证收到的 HTTP 请求是否包含格式正确的所需数据。前任。 电子邮件 字段是必需的,并且应该是有效的电子邮件地址**
特定域验证 - 验证 Command
尝试应用于当前应用程序状态的修改在 预期域 中是否有效。前任。 电子邮件 必须是唯一的。 电子邮件 不应列入黑名单。
第一种验证进入 ActionFilter
。
第二种验证进入 IPipelineBehavior
我在我的 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();
}
}
一切正常,但我真的很希望能够 return 在结束响应中进行验证。我正在努力进行这样的更改,要么编译它,要么不让 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()
但是尝试 return 一个 CommandResult 而不是抛出异常给我带来了类型转换问题,感觉我把它弄得太复杂了,我遗漏了一些非常基本的东西。
让我为您的问题建议另一种方法。在您的请求命中控制器并且必须由 mediatr 路由之前,使用自定义 ActionFilterAttribute 而不是管道来执行验证。以下示例使用 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);
}
}
}
有两种种验证(至少)
请求数据验证 - 验证收到的 HTTP 请求是否包含格式正确的所需数据。前任。 电子邮件 字段是必需的,并且应该是有效的电子邮件地址**
特定域验证 - 验证
Command
尝试应用于当前应用程序状态的修改在 预期域 中是否有效。前任。 电子邮件 必须是唯一的。 电子邮件 不应列入黑名单。
第一种验证进入 ActionFilter
。
第二种验证进入 IPipelineBehavior