如果 ExceptionHandler 捕获到异常,则不会调用 DelegatingHandler
DelegatingHandler not getting invoked if exception caught by ExceptionHandler
我正在尝试了解 Web API Http 管道的工作原理!
在我的 Web API 项目中,我使用以下技术来 log/handle 异常:
- 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
中收到响应?
Solution: (Updated 10/09/2018)
好的,所以我找到了解决方案
通过修改 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);
}
}
问题:
- 我还是不明白它是如何工作的?
IExceptionHandler
和 ExceptionHandler
有什么区别?
有人能解释一下吗?
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 基础 class 假定您可能只想处理异常在执行堆栈的顶层。我见过 other handlers like CORS get in the way of this 的情况,并且 catch 块永远不会位于顶层。
如果这是您所看到的,您仍然可以扩展 ExceptionHandler,并将 ShouldHandle 方法重写为始终 return true。或者你可以更外科手术并专门检测 CORS 是否可能妨碍顶级检查 as suggested in this comment.
我正在尝试了解 Web API Http 管道的工作原理!
在我的 Web API 项目中,我使用以下技术来 log/handle 异常:
- 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
中收到响应?
Solution: (Updated 10/09/2018)
好的,所以我找到了解决方案
通过修改 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);
}
}
问题:
- 我还是不明白它是如何工作的?
IExceptionHandler
和ExceptionHandler
有什么区别?
有人能解释一下吗?
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 基础 class 假定您可能只想处理异常在执行堆栈的顶层。我见过 other handlers like CORS get in the way of this 的情况,并且 catch 块永远不会位于顶层。
如果这是您所看到的,您仍然可以扩展 ExceptionHandler,并将 ShouldHandle 方法重写为始终 return true。或者你可以更外科手术并专门检测 CORS 是否可能妨碍顶级检查 as suggested in this comment.