来自管道行为的 MediatR 流畅的验证响应

Posted

技术标签:

【中文标题】来自管道行为的 MediatR 流畅的验证响应【英文标题】:MediatR fluent validation response from pipeline behavior 【发布时间】:2019-01-09 06:08:54 【问题描述】:

我有一个 MediatR 管道行为,用于使用 FluentValidation 库验证命令。我见过很多例子,你从行为中抛出一个 ValidationException,这对我来说很好。但是在我的场景中,我想用验证错误更新我的响应对象。

我能够构建和运行以下代码。当我在 if 语句中设置断点时,CommandResponse 按预期构造了验证错误 - 但是当原始调用者收到响应时,它为空:

public class RequestValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IRequest<TResponse>

    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public RequestValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
    
         _validators = validators;
    

    public Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    
        var context = new ValidationContext(request);

        // Run the associated validator against the request
        var failures = _validators
            .Select(v => v.Validate(context))
            .SelectMany(result => result.Errors)
            .Where(f => f != null)
            .ToList();

        if(failures.Count != 0)
        
            var commandResponse = new CommandResponse(failures)  isSuccess = false ;
            return commandResponse as Task<TResponse>;
        
        else
           
            return next();
        
    

我认为这与我尝试将其转换为任务有关 - 但没有这个我会得到编译器错误。如果验证通过,我将返回与我的命令处理程序相同的类型,所以我不知道为什么它返回预期响应的空实例。我觉得有更好的方法来处理这个问题,但我尝试了许多变体都无济于事。有什么建议?有没有更好的模式可以使用?我希望将其保留在管道中,因为它会被大量重复使用。

【问题讨论】:

Handle 方法应该是async 吗?然后你就不需要演员了 Task 【参考方案1】:

我最终将异常处理中间件添加到 MVC 项目中。我没有尝试将验证错误作为对象传回,而是在管道行为中抛出一个 ValidationException ,中间件处理整个项目中的所有异常。这实际上效果更好,因为我在处理链中更高的一个位置处理所有异常。

这是我发布的代码的更新部分:

if(failures.Count != 0)

    // If any failures are found, throw a custom ValidationException object
    throw new ValidationException(failures);

else
   
    // If validation passed, allow the command or query to continue:
    return next();

这里是异常处理中间件:

public class ErrorHandlingMiddleware

    private readonly RequestDelegate next;

    public ErrorHandlingMiddleware(RequestDelegate next)
    
        this.next = next;
    

    public async Task Invoke(HttpContext context /* other dependencies */)
    
        try
        
            await next(context);
        
        catch (Exception ex)
        
            await HandleExceptionAsync(context, ex);
        
    


    private static Task HandleExceptionAsync(HttpContext context, Exception exception)
    
        // Log issues and handle exception response

        if (exception.GetType() == typeof(ValidationException))
        
            var code = HttpStatusCode.BadRequest;
            var result = JsonConvert.SerializeObject(((ValidationException)exception).Failures);
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = (int)code;
            return context.Response.WriteAsync(result);

        
        else
        
            var code = HttpStatusCode.InternalServerError;
            var result = JsonConvert.SerializeObject(new  isSuccess = false, error = exception.Message );
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = (int)code;
            return context.Response.WriteAsync(result);
        
    

然后在添加 MVC 之前在 Startup 中注册中间件:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)

    app.UseMiddleware(typeof(ErrorHandlingMiddleware));
    app.UseMvc();

注意:您还可以为中间件创建扩展方法:

public static class ErrorHandlingMiddlewareExtension

    public static IApplicationBuilder UseErrorHandlingMiddleware(
        this IApplicationBuilder builder)
    
        return builder.UseMiddleware<ErrorHandlingMiddleware>();
    

这允许您像这样注册它:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)

    app.UseErrorHandlingMiddleware();
    app.UseMvc();

【讨论】:

我在 Startup 的 Configure 方法的开头添加了这个中间件,但是在抛出异常时它不会被调用。我正在使用.Net Core 3.1。我错过了什么吗? 您能否验证您是否正在抛出异常以便中间件捕获它?确保您不只是允许异常静默失败并继续运行剩余的进程。如果看起来不错,可以发布您的代码以便对其进行审核? 代码对我来说是异常管理反模式的味道。处理异常非常慢。【参考方案2】:

我使用的是.Net core 3.1,当我在StartupConfigure函数中的以下块之前添加中间件时无法捕获异常

  if (env.IsDevelopment())          
  
    app.UseDeveloperExceptionPage();
  

检查配置方法。确保在上面这样的声明之后注册它。这很明显,但可以帮助像我这样的人。

 if (env.IsDevelopment())
 
    app.UseDeveloperExceptionPage();
 
 app.UseMiddleware<ErrorHandlingMiddleware>(); 

【讨论】:

以上是关于来自管道行为的 MediatR 流畅的验证响应的主要内容,如果未能解决你的问题,请参考以下文章

Mediatr 行为管道中的验证

Mediatr 3.0 使用管道行为进行身份验证

向 MediatR 行为管道添加验证?

使用mediatR管道行为添加验证。

Mediatr:单元测试行为/验证

使用 void/Task 响应注册 MediatR 管道