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

Posted

技术标签:

【中文标题】返回带有错误的响应,而不是在验证管道 mediatr 3 中抛出异常【英文标题】:Return response with errors instead of throwing exception in validation pipeline 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,它现在可以工作了!非常感谢。 【参考方案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

【讨论】:

这不是被问到的。他们已经知道如何从管道中抛出验证异常。他们特别询问如何不抛出异常,而是返回错误。

以上是关于返回带有错误的响应,而不是在验证管道 mediatr 3 中抛出异常的主要内容,如果未能解决你的问题,请参考以下文章

Gitlab管道失败:错误:准备失败:来自守护进程的错误响应:toomanyrequests

重定向到带有 SAML 响应的回调 url 时出现错误无法以角度发布

如何自定义响应错误 400 错误请求

为啥“”.abcd 返回未定义的值而不是在 Javascript 中抛出未定义的错误(但 Typescript 抛出警告)

如何返回带有错误响应的 Http 状态行?

带有 beta 运行时的 Javascript Azure 函数不返回错误 HTTP 响应的响应正文