和fluent验证 i最初遵循了这篇文章,但没有重新发明我开始使用流利验证管道中的流动性的车轮。基本上,来自我的CQRS查询的所有响应都包裹在结果对象中,这避免了必须处理异常作为错误手持的方法。 ,但是,我无法让我的管道玩得很好:
public class ValidationPipeline<TRequest, TResponse>
: IPipelineBehavior<TRequest, TResponse>
where TResponse : class
where TRequest : IRequest<TResponse>
{
private readonly IValidator<TRequest> _compositeValidator;
public ValidationPipeline(IValidator<TRequest> compositeValidator)
{
_compositeValidator = compositeValidator;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var result = await _compositeValidator.ValidateAsync(request, cancellationToken);
if (!result.IsValid)
{
Error error = new Error();
var responseType = typeof(TResponse);
foreach (var validationFailure in result.Errors)
{
Log.Warning($"{responseType} - {validationFailure.ErrorMessage}");
error.Reasons.Add(new Error(validationFailure.ErrorMessage));
}
// This always returns null instead of a Result with errors in it.
var f = Result.Fail(error) as TResponse;
return f;
}
return await next();
}
}
我还必须以某种方式将结果对象转换回Tresponse,在其中tresponse始终是结果
任何建议都非常感谢!
Edit:autofac集成
protected override void Load(ContainerBuilder builder)
{
var assembly = Assembly.GetExecutingAssembly();
// MediatR
builder.AddMediatR(assembly);
// Register the Command's Validators (Validators based on FluentValidation library)
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.IsClosedTypeOf(typeof(IValidator<>)))
.AsImplementedInterfaces();
// Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands
builder.RegisterAssemblyTypes(assembly)
.AsClosedTypesOf(typeof(IRequestHandler<,>));
// Register Behavior Pipeline
builder.RegisterGeneric(typeof(ValidationPipeline<,>)).As(typeof(IPipelineBehavior<,>));
}
您应该更改 there tresponse:class
tothere tresponse:结果
我正在编辑我的帖子,但是我认为这几乎是我问题的答案。
我已经能够将问题隔离到以下行:
var f = Result.Fail(error).ToResult<CustomClass>() as TResponse;
return f;
如果我很难将我传递给结果的课程,那么转换就可以按预期工作,一切都应根据。现在的问题变成了,如何获得可以从tresponse中传递到.ToResult<T>()
?
我一直在寻找答案,但是从根本上讲,在运行时,我在编译时需要它需要它的时间类参考是不可能的。 var resultType = typeof(TResponse).GetGenericArguments()[0];
var invalidResponseType = typeof(ValidateableResponse<>).MakeGenericType(resultType);
var f = Activator.CreateInstance(invalidResponseType, null) as TResponse;
return f;
这将起作用,但是结果对象具有封闭的构造函数,因此我有一个例外。我已经在FluentResult的Github上留下了一个问题,也许可以更改。
现在我有一个解决方法,我让我的处理程序从basehandler继承BaseHandler:
public class BaseHandler
{
public Result Validate<TQuery, TValidator>(TQuery request) where TValidator : AbstractValidator<TQuery>
{
var validator = (TValidator)Activator.CreateInstance(typeof(TValidator));
var result = validator.Validate(request);
return CreateResult(result);
}
public async Task<Result> ValidateAsync<TQuery, TValidator>(TQuery request) where TValidator : AbstractValidator<TQuery>
{
var validator = (TValidator)Activator.CreateInstance(typeof(TValidator));
var result = await validator.ValidateAsync(request);
return CreateResult(result);
}
private Result CreateResult(ValidationResult result)
{
if (!result.IsValid)
{
if (result.Errors.Count == 1)
{
string msg = $"Validation Failure: {result.Errors.First().ErrorMessage}";
return Result.Fail(msg);
}
Error error = new Error("Validation Failure");
foreach (var validationFailure in result.Errors)
{
Log.Warning($"{validationFailure.ErrorMessage}");
error.Reasons.Add(new Error(validationFailure.ErrorMessage));
}
return Result.Fail(error);
}
return Result.Ok();
}
}
usage示例:
// At the top of my handle function
var result = await ValidateAsync<GetItemByIdQuery, GetItemByIdQueryValidator>(request);
if (result.IsFailed) return result;
这也起作用,唯一的缺点是我必须在每个句柄功能的顶部添加它才能启用验证。
现在很好,我将拭目以待,看看是否可以更新FluentResult软件包,以便我可以尝试较早的建议。感谢所有人的建议!
如果我理解您在问什么,我认为我做了与您要做的同一件事。以下是我解决这个问题的解决方案。
public class ValidationPipeline<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TResponse : ResultBase<TResponse>, new() where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationPipeline(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators ?? throw new ArgumentNullException(nameof(validators));
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken,
RequestHandlerDelegate<TResponse> next)
{
var validationFailures = _validators.Select(validator => validator.Validate(request))
.SelectMany(validationResult => validationResult.Errors)
.Where(validationFailure => validationFailure != null).ToList();
if (validationFailures.Any())
{
var responseType = typeof(TResponse);
TResponse invalidResponse;
if (responseType.IsGenericType)
{
var resultType = responseType.GetGenericArguments()[0];
var invalidResponseType = typeof(Result<>).MakeGenericType(resultType);
invalidResponse = Activator.CreateInstance(invalidResponseType, null) as TResponse;
}
else
{
invalidResponse = new TResponse();
}
invalidResponse.WithErrors(validationFailures.Select(s => s.ErrorMessage));
return invalidResponse;
}
return await next();
}
}
我也有类似的问题。此外,有些处理程序返回了
Result
,而其他处理程序却是
Result<ABC>
或
Result<XYZ>
。通用行为无法处理这一点。
两个解决方法:归还返回的
像此一样,分别注册每个处理程序和管道
Result
也许有点晚了,但这是我所有请求都返回结果或结果的管道解决方案。
services.AddTransient( typeof(IPipelineBehavior<MyCommand,Result<MyCommandResult>>), typeof(MyBehavior<MyCommand, MyCommandResult>));
我使用的默认错误。
public class FluentValidationBehaviour<TRequest, TResponse>
: IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
where TResponse : ResultBase<TResponse>, new()
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public FluentValidationBehaviour(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public async Task<TResponse> Handle(
TRequest request,
RequestHandlerDelegate<TResponse> next,
CancellationToken cancellationToken)
{
if (!_validators.Any())
return await next();
var context = new ValidationContext<TRequest>(request);
var validationResults = await Task.WhenAll(_validators.Select(validator =>
validator.ValidateAsync(context, cancellationToken)));
var failures = validationResults
.SelectMany(result => result.Errors)
.GroupBy(error => error.PropertyName)
.ToDictionary(
group => group.Key,
group => group.Select(error => error.ErrorMessage)
);
if (failures.Count != 0)
return new TResponse()
.WithError(new BadRequestError("Erro de validação", failures));
return await next();
}
}
Request示例
public abstract class ApiError<TExtension> : Error
{
protected ApiError(
HttpStatusCode statusCode,
string? detail = null,
TExtension? extensions = default,
ResultErrorExtensions extensionsType = ResultErrorExtensions.Errors)
: base(detail)
{
Metadata.Add("Title", statusCode);
Metadata.Add("StatusCode", (int)statusCode);
if (extensions != null)
Metadata.Add(extensionsType.ToString(), extensions);
}
}
public sealed class BadRequestError(string detail, object? errors = null)
: ApiError<object>(HttpStatusCode.BadRequest, detail, errors);