如果 ExceptionHandler 捕获到异常,则不会调用 DelegatingHandler

Posted

技术标签:

【中文标题】如果 ExceptionHandler 捕获到异常,则不会调用 DelegatingHandler【英文标题】:DelegatingHandler not getting invoked if exception caught by ExceptionHandler 【发布时间】:2019-03-13 17:25:54 【问题描述】:

我正在尝试了解 Web API Http 管道的工作原理!

在我的 Web API 项目中,我使用以下技术来记录/处理异常:

ExceptionHandler -> 在全局级别处理异常 ExceptionFilterAttribute -> 处理用户抛出的自定义异常 DelegatingHandler -> 记录请求和响应数据

每个实现的示例代码:

异常过滤器:

public class CustomExceptionFilter : ExceptionFilterAttribute

    public override void OnException(HttpActionExecutedContext context)
    
        var request = context.ActionContext.Request;
        if (context.Exception is ItemNotFoundException)
        
            context.Response = request.CreateResponse(HttpStatusCode.NotFound, context.Exception.Message);
        
        else if (context.Exception is InvalidRequestException)
        
            context.Response = request.CreateResponse(HttpStatusCode.BadRequest, context.Exception.Message);
        
    

异常处理程序:

public class GlobalExceptionHandler : ExceptionHandler

    public override void Handle(ExceptionHandlerContext context)
    
        var result = new HttpResponseMessage(HttpStatusCode.InternalServerError)
        
            Content = new StringContent(Constant.ErrorMessage.InternalServerError)
        ;
        context.Result = new ErrorMessageResult(context.Request, result);
    


public class ErrorMessageResult : IHttpActionResult

    private readonly HttpRequestMessage _request;
    private readonly HttpResponseMessage _httpResponseMessage;

    public ErrorMessageResult(HttpRequestMessage request, HttpResponseMessage httpResponseMessage)
    
        _request = request;
        _httpResponseMessage = httpResponseMessage;
    

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    
        return Task.FromResult(_httpResponseMessage);
    

委托处理程序:

public class LogRequestAndResponseHandler : DelegatingHandler

    private readonly ILoggingService _loggingService;

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    
        string requestBody = await request.Content.ReadAsStringAsync();
        _loggingService.FirstLevelServiceLog(requestBody);
        var result = await base.SendAsync(request, cancellationToken);
        if (result.Content != null)
        
            var responseBody = await result.Content.ReadAsStringAsync();
            _loggingService.FirstLevelServiceLog(responseBody);
        
        return result;
    

观察:

当有一个自定义异常 CustomExceptionFilter 被调用,然后响应记录在 LogRequestAndResponseHandler 中。 但是,如果不处理异常,它会进入 GlobalExceptionHandler,然后响应不会到达 LogRequestAndResponseHandler 进行日志记录。

谁能告诉我,必须在CustomExceptionFilter/GlobalExceptionHandler 中进行哪些代码更改才能收到DelegatingHandler 中的响应?

解决方案:(2018 年 10 月 9 日更新)

好的,所以我找到了解决方案here

通过修改ExceptionHandler 代码,我可以捕捉到DelegatingHandler 中的响应

关键是继承自 IExceptionHandler 而不是 ExceptionHandler

代码:

public class GlobalExceptionHandler : IExceptionHandler

    public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
    
        var httpResponse = context.Request.CreateResponse(HttpStatusCode.InternalServerError, Constant.ErrorMessage.InternalServerError);
        context.Result = new ResponseMessageResult(httpResponse);

        return Task.FromResult(0);
    

问题:

我仍然无法理解它是如何工作的? IExceptionHandlerExceptionHandler 有什么区别?

有人能解释一下吗?

【问题讨论】:

【参考方案1】:

ExceptionHandler 像这样实现IExceptionHandler

Task IExceptionHandler.HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)

  if (context == null)
    throw new ArgumentNullException(nameof (context));
  ExceptionContext exceptionContext = context.ExceptionContext;
  if (!this.ShouldHandle(context))
    return TaskHelpers.Completed();
  return this.HandleAsync(context, cancellationToken);

我怀疑您看到的区别在于 ShouldHandle 检查,它的实现方式如下:

public virtual bool ShouldHandle(ExceptionHandlerContext context)

  if (context == null)
    throw new ArgumentNullException(nameof (context));
  return context.ExceptionContext.CatchBlock.IsTopLevel;

我对管道不是很熟悉,但从我所看到的情况来看,似乎可以在不同的点处理异常,并且 ExceptionHandler 基类假设您可能只想在顶层处理异常执行栈。我见过other handlers like CORS get in the way of this 和catch 块永远不会位于顶层的情况。

如果这是您所看到的,您仍然可以扩展 ExceptionHandler,并覆盖 ShouldHandle 方法以始终返回 true。或者您可以更加外科手术并专门检测 CORS 是否可能妨碍***检查 as suggested in this comment。

【讨论】:

以上是关于如果 ExceptionHandler 捕获到异常,则不会调用 DelegatingHandler的主要内容,如果未能解决你的问题,请参考以下文章

如何使用@ExceptionHandler 捕获 HTTP 405“不允许的方法”异常?

统一异常处理@ExceptionHandler

Spring的 @ExceptionHandler注解无效问题

SpringBoot全局异常捕获

如何覆盖 $exceptionHandler 实现

Java Spring ExceptionHandler Controller 语法和良好实践