Asp.net core 如何在Mediatr中为所有请求定义一个特定的异常处理程序

Asp.net core 如何在Mediatr中为所有请求定义一个特定的异常处理程序,asp.net-core,autofac,mediatr,Asp.net Core,Autofac,Mediatr,我将Mediatr用于ASP.NET核心项目来处理所有请求。我实现了几个请求/响应/处理程序。它们中的每一个都可以抛出一个特定的异常,我们称之为“MyException”类。 我将异常处理程序定义为 public class MyExceptionHandler : RequestExceptionHandler<MyRequest<MyResponse>, MyResponse, MyException> { protected override void Ha

我将Mediatr用于ASP.NET核心项目来处理所有请求。我实现了几个请求/响应/处理程序。它们中的每一个都可以抛出一个特定的异常,我们称之为“MyException”类。 我将异常处理程序定义为

public class MyExceptionHandler : RequestExceptionHandler<MyRequest<MyResponse>, MyResponse, MyException>
{
    protected override void Handle(MyRequest<MyResponse> request, MyException exception, RequestExceptionHandlerState<MyResponse> state)
    {
        MyResponse response = new MyResponse();
        //Set some specific properties here in the response to indicate an error occurred
        state.SetHandled(response);
    }
}
公共类MyExceptionHandler:RequestExceptionHandler
{
受保护的覆盖无效句柄(MyRequest请求、MyException异常、RequestExceptionHandlerState状态)
{
MyResponse=新的MyResponse();
//在响应中设置一些特定属性以指示发生错误
state.SetHandled(响应);
}
}
我将这个异常处理程序添加到(Autofac)依赖项容器中,如果我在MyRequest和MyResponse的MyHandler中抛出一个MyException,这将起作用。但是,我有几十个请求、响应和相应的处理程序。那么,我如何为所有这些异常注册这个异常处理程序来处理这个特定的异常(注意:所有响应都来自同一个基类)。它尝试了下面这样的方法,但是没有调用。它只有在我给出实际的类型时才起作用,但这意味着我必须为每个类型创建一个异常处理程序,这是不实际的。有没有办法解决这个问题

   public class MyExceptionHandler : RequestExceptionHandler<IRequest<BaseResponse>, BaseResponse, MyException>
{
    protected override void Handle(IRequest<BaseResponse> request, MyException exception, RequestExceptionHandlerState<BaseResponse> state)
    {
        BaseResponse response = new BaseResponse();
        //Set some specific properties here in the response to indicate an error occurred
        state.SetHandled(response);
    }
}
公共类MyExceptionHandler:RequestExceptionHandler
{
受保护的覆盖无效句柄(IRequest请求、MyException异常、RequestExceptionHandlerState状态)
{
BaseResponse=新的BaseResponse();
//在响应中设置一些特定属性以指示发生错误
state.SetHandled(响应);
}
}

当MediatR处理程序中抛出相同类型的异常时,另一种方法是放弃MediatR异常处理程序,并通过中间件全局处理它。这样做可以确保同一段错误处理代码不会分散在多个地方

当然,您应该考虑一下,您是否总是(以您的最佳知识)以完全相同的方式处理异常,因为如果不是,这意味着如果您开始检查同一异常上的自定义条件,中间件解决方案将极有可能使您的代码变得不可维护和干净。 下面是一个中间件示例:

public class ValidationExceptionHandlerMiddleware
{
    private readonly RequestDelegate next;

    public ValidationExceptionHandlerMiddleware(RequestDelegate next) => this.next = next;

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await this.next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        switch (exception)
        {
            string result = "";
            case MyException validationException:
                //custom handling of my exception
                context.Response.ContentType = "application/json";
                context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                result = //your serialized json object with error data
                break;
            default:
                context.Response.ContentType = "application/json";
                context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                result = //your serialized json object with error data
                break;
        }

        return context.Response.WriteAsync(result);
    }
}
然后您需要做的是在您认为合适的管道中注册这个中间件。你必须在你想要处理的中间产品之前注册它

您可以在
Startup.cs中的
Configure()
方法中执行此操作:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...
    builder.UseMiddleware<ValidationExceptionHandlerMiddleware>();
    ...
}
public void配置(IApplicationBuilder应用程序,IWebHostEnvironment环境)
{
...
builder.UseMiddleware();
...
}
因此,现在,应用程序中所有抛出的和未处理的异常都将由中间件处理


请注意,这是一个直接写入响应的低级解决方案,因此,如果在处理异常时需要执行业务逻辑,那么这可能不合适。

AFAIK,处理程序用于特定类型的请求、响应和异常<在这种情况下,code>MediatR
无法执行通用变量,因为这是在发生异常时执行的

所以基本上,你所做的和你想要达到的非常接近。您可以创建一个泛型抽象处理程序,并根据需要拥有尽可能多的派生类型化实现,但您必须始终创建该样板文件。这里是捕获
InvalidOperationException
的处理程序的示例

公共类基类响应
{
公共布尔错误{get;set;}
公共字符串ErrorMessage{get;set;}=null!;
}
公共类SomeResponse:BaseResponse{}
公共类BaseRequest:IRequest,其中响应:BaseResponse{}
公共类SomeRequest:BaseRequest{}
公共类SomeRequestHandler:IRequestHandler
{
公共任务句柄(SomeRequest请求、CancellationToken CancellationToken)
{
抛出新的InvalidOperationException();
}
}
公共抽象类
AbstractInvalidOperationExceptionHandler:RequestExceptionHandler
其中TRequest:BaseRequest
其中treresponse:BaseResponse,new()
{
受保护的覆盖无效句柄(TRequest请求、InvalidOperationException异常、RequestExceptionHandlerState状态)
{
var响应=新的响应
{
错误=真,
ErrorMessage=异常。消息
};
state.SetHandled(响应);
}
}
公共类SomeInvalidOperationExceptionHandler:AbstractInvalidOperationExceptionHandler
{
}
这应该可以根据需要工作,需要为
TRequest
+
treresponse
的每个组合添加一个派生类型。如果不使用,则还必须手动注册这些派生类型中的每一个,它使用程序集扫描来注册
RequestExceptionHandler
的所有实现

还有第二个选项,与建议的中间件类似,但不是编写中间件,而是为接口的每个异常创建一个实现
IExceptionFilter
(或者
iasyncceptionfilter
,如果这里需要
async

如果再次处理类型为
invalidoOperationFilter
的异常,它看起来会是这样的:

公共类InvalidOperationExceptionFilter:IEExceptionFilter
{
公共无效OneException(ExceptionContext上下文)
{
if(context.Exception不是InvalidOperationException InvalidOperationException)
{
返回;
}
context.ExceptionHandled=true;//这与MediatR遵循相同的原则,需要将其设置为handled,管道才能停止执行!
//这将把响应设置为409
context.Result=新的ConflictObjectResult(新的
{
信息=无效