【问题标题】:Return response with errors instead of throwing exception in validation pipeline mediatr 3返回带有错误的响应,而不是在验证管道 mediatr 3 中抛出异常
【发布时间】:2017-10-19 05:35:28
【问题描述】:

我目前正在使用 Mediatr 3 中的管道行为进行请求验证。如果发生任何故障,我遇到的所有示例都会抛出 ValidationException,而不是这样做,我想返回带有错误的响应。有人知道怎么做吗?

以下是验证管道的代码:

public class ValidationPipeline<TRequest, TResponse> :
    IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public ValidationPipeline(IEnumerable<IValidator<TRequest>> validators)
    {
        _validators = validators;
    }

    public Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
    {
        var failures = _validators
            .Select(v => v.Validate(request))
            .SelectMany(result => result.Errors)
            .Where(f => f != null)
            .ToList();

        if (failures.Any())
        {
            throw new ValidationException(failures);
        }

        return next();
    }
}

注意:我发现了这个问题Handling errors/exceptions in a mediator pipeline using CQRS?,我对答案中的第一个选项感兴趣,但没有关于如何做到这一点的明确示例。

这是我的响应类:

public class ResponseBase : ValidationResult
{
    public ResponseBase() : base() { }

    public ResponseBase(IEnumerable<ValidationFailure> failures) : base(failures)  {
    }
}

我在验证管道类中添加了以下签名:

public class ValidationPipeline<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse> 
where TResponse : ResponseBase

然后我在 Handle 方法中这样做了:

var response = new ResponseBase(failures);
return Task.FromResult<TResponse>(response);

但这给了我错误“无法转换为 TResponse”。

【问题讨论】:

  • return the response with the error - response 是什么样的?
  • 管道TResponse 将与底层处理程序TResponse 相同——ResponseBase : ValidationResult 对我来说就像是代码味道。我不清楚您要达到的目标。
  • 我正在尝试从 FluentValidation 的 ValidationResult 类继承
  • 这意味着整个管道(包括您的主处理程序)将返回这种类型,对吗?
  • 是的,完全正确。感谢您指出了这一点。所以我决定不从任何类继承 ResponseBase,它现在可以工作了!非常感谢。

标签: c# asp.net-mvc cqrs mediatr


【解决方案1】:

几年前,我创建了通用的 Result 对象,我正在不断改进它。很简单,查看https://github.com/martinbrabec/mbtools

如果您可以接受 Result(或 Result)作为应用层中的返回类型每个方法,那么您可以像这样使用 ValidationBehavior:

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
    where TResponse : Result, new()
{
    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)
    {
        if (_validators.Any())
        {
            var context = new ValidationContext(request);

            List<ValidationFailure> failures = _validators
                .Select(v => v.Validate(context))
                .SelectMany(result => result.Errors)
                .Where(f => f != null)
                .ToList();

            if (failures.Any())
            {
                TResponse response = new TResponse();

                response.Set(ErrorType.NotValid, failures.Select(s => s.ErrorMessage), null);

                return Task.FromResult<TResponse>(response);
            }
            else
            {
                return next();
            }
        }

        return next();
    }

}

由于您的所有处理程序都返回 Result(或基于 Result 的 Result),您将能够毫无例外地处理所有验证错误。

【讨论】:

    【解决方案2】:

    如果有任何故障,请不要致电next

    public Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next)
    {
        var failures = _validators
            .Select(v => v.Validate(request))
            .SelectMany(result => result.Errors)
            .Where(f => f != null)
            .ToList();
    
        if (failures.Any())
        {
            var response = new Thing(); //obviously a type conforming to TResponse
            response.Failures = failures; //I'm making an assumption on the property name here.
    
            return Task.FromResult(response);
        }
        else
        {
            return next();
        }
    }
    

    注意:
    你的班级(在我的例子中是Thing)必须是TResponse类型的

    【讨论】:

    • 嗨,我会在这里回答你上面的问题。我的响应类如下所示:public class ResponseBase : ValidationResult { public ResponseBase() : base() { } public ResponseBase(IEnumerable&lt;ValidationFailure&gt; failures) : base(failures) { } } 我这样做了,但是尝试将 ResponseBase 转换为 TResponse 时出现错误 var response = new ResponseBase(failures); return Task.FromResult(response);
    • 我正在处理它,仍然在为格式化而苦恼,抱歉:/
    • 这里的主要问题是 TResponse 并不总是已知的,或者在某些情况下是通用类型(这是一个设计决定)
    【解决方案3】:

    您可以使用包配置验证处理 https://www.nuget.org/packages/MediatR.Extensions.FluentValidation.AspNetCore

    只需在配置部分插入:

    services.AddFluentValidation(new[] {typeof(GenerateInvoiceHandler).GetTypeInfo().Assembly});
    

    GitHub

    【讨论】:

    • 这不是被问到的。他们已经知道如何从管道中抛出验证异常。他们特别询问如何不抛出异常,而是返回错误。
    猜你喜欢
    • 2014-06-03
    • 2021-01-27
    • 2011-08-28
    • 2013-06-09
    • 1970-01-01
    • 1970-01-01
    • 2018-04-23
    • 2014-07-27
    • 1970-01-01
    相关资源
    最近更新 更多