在 ASP.NET Web API 2 中禁用*所有*异常处理(为我自己腾出空间)? [英] Disable *all* exception handling in ASP.NET Web API 2 (to make room for my own)?
问题描述
我想在中间件组件中连接异常处理,如下所示:
public override async Task Invoke(IOwinContext context){尝试{等待 Next.Invoke(context);}捕获(异常前){//记录错误并返回 500 响应}}
但是,我想捕获的一些异常在我到达它们之前被 Web API 管道捕获并转换为 HttpErrorResponse
.在这个过程中,我丢失了很多关于错误的细节,所以在调试等时我无法获得有用的堆栈跟踪(当抛出异常时调试器甚至没有停止 - 我必须手动单步执行代码并查看它失败的地方......).
我尝试使用以下实现添加自定义异常处理程序:
public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancelToken){var owinContext = context.Request.GetOwinContext();owinContext.Set(Constants.ContextKeys.Exception, context.Exception);返回 Task.FromResult(0);}
在我的启动配置中通过config.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler());
注册的,但是执行完Next.Invoke(context)
通过
context.Get(Constants.ContextKeys.Exception);
仍然没有给我我想要的所有细节,也没有在调试器的故障点处停下来.
有没有办法可以完全关闭所有内置错误处理,以便我自己的中间件可以处理它?</p>
澄清,因为很多人似乎误解了我的意思:
- Web API 中的内置错误处理可以捕获一些(但不是全部)异常并将它们重写为 500 个响应.
- 我想捕获所有异常,做一些日志记录,然后然后用我选择的信息发出 500 个响应(对于大多数,见下一个要点).
- 还有一些异常表示业务逻辑错误,我想为此返回 40x 错误.
- 我希望它位于(应用程序)管道的顶部,即在请求生命周期中包装所有内容
- 我想使用 OWIN 来处理这个问题,使其可移植到未来可能的自托管场景中(即,这个应用程序将始终托管在 IIS 上并不是一成不变的 - HTTP 模块、Global.asax.cs 等与此处无关).
更新:我写了关于这个的博客.在研究博客文章时,我发现了一些改进的潜力;我已经更新了这个答案的相关部分.有关为什么我认为这比此处的所有其他建议或默认行为更好的更多详细信息,请阅读整篇文章:)
<小时>我现在采用了以下方法,即使不是 100% 符合我的要求,它似乎也能正常工作:
创建一个类
PassthroughExceptionHandler
:公共类 PassthroughExceptionHandler : IExceptionHandler{公共任务 HandleAsync(ExceptionHandlerContext 上下文,CancellationToken 取消令牌){//不要只是抛出异常;这将破坏堆栈跟踪var info = ExceptionDispatchInfo.Capture(context.Exception);信息.抛出();返回 Task.CompletedTask;}}
让那个类替换Web API的
IExceptionHandler
服务:config.Services.Replace(typeof(IExceptionHandler), new PassthroughExceptionHandler());
创建一个执行我想要的中间件类:
公共类ExceptionHandlerMiddleware{公共覆盖异步任务调用(IOwinContext 上下文){尝试{等待下一个?.调用(上下文);}捕获(异常前){//处理和/或日志}}}
首先在堆栈中注册该中间件:
app.Use
().UseStageMarker(PipelineStage.Authenticate)//为简洁起见省略了其他中间件.UseStageMarker(PipelineStage.PreHandlerExecute).UseWebApi(config);
我仍然会将赏金奖励给任何提出的人(赏金已过期......)我仍在寻找更好的解决方案,例如,当中断时未处理的 异常被抛出.(这种方法使VS在处理程序中重新抛出异常时中断,但原始调用堆栈丢失;我必须在故障行设置断点并再次调试才能在抛出异常时拦截状态.)
I want to wire up exception handling in a middleware component, something like this:
public override async Task Invoke(IOwinContext context)
{
try
{
await Next.Invoke(context);
}
catch (Exception ex)
{
// Log error and return 500 response
}
}
However, some of the exceptions I would like to catch are being caught and converted to HttpErrorResponse
s by the Web API pipeline before I can get to them. In the process, I lose a lot of details about the errors, so I can't get useful stack traces when debugging etc (the debugger doesn't even stop when the exception is thrown - I have to manually step through the code and see where it fails...).
I tried adding a custom exception handler with the following implementation:
public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
var owinContext = context.Request.GetOwinContext();
owinContext.Set(Constants.ContextKeys.Exception, context.Exception);
return Task.FromResult(0);
}
registered through config.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler());
in my startup configuration, but looking at it after executing Next.Invoke(context)
through
context.Get<Exception>(Constants.ContextKeys.Exception);
still doesn't give me all the detail I want, as well as failing to stop at the fault point with the debugger.
Is there a way I can completely turn off all built-in error handling, so that my own middleware can take care of it?
Clarification, since a lot of people seem to misunderstand what I'm after:
- The built-in error handling in Web API catches some (but not all) exceptions and rewrites them into 500 responses.
- I want to catch all exceptions, do some logging, and then emit 500 responses with the information I choose (for most of them, see next bullet).
- There are also some exceptions that signal business logic faults, for which I want to return 40x errors instead.
- I want this to be at the top of the (app) pipeline, i.e. wrapping everything else in the request lifecycle
- I want to handle this using OWIN, to make it portable to a possible future self-hosted scenario (i.e. it's not written in stone that this app will always be hosted on IIS - HTTP modules, Global.asax.cs et al are not relevant here).
Update: I blogged about this. When researching the blog post, I found some potential for improvement; I've updated the relevant parts of this answer. For more detail on why I think this is better than all other suggestions here, or the default behavior, read the entire post :)
I have now gone with the following approach, which seems to work OK, even if not 100 % compliant with what I was looking for:
Create a class
PassthroughExceptionHandler
:public class PassthroughExceptionHandler : IExceptionHandler { public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken) { // don't just throw the exception; that will ruin the stack trace var info = ExceptionDispatchInfo.Capture(context.Exception); info.Throw(); return Task.CompletedTask; } }
Let that class replace the
IExceptionHandler
service of Web API:config.Services.Replace(typeof(IExceptionHandler), new PassthroughExceptionHandler());
Create a middleware class which does what I want:
public class ExceptionHandlerMiddleware { public override async Task Invoke(IOwinContext context) { try { await Next?.Invoke(context); } catch (Exception ex) { // handle and/or log } } }
Register that middleware first in the stack:
app.Use<ExceptionHandlerMiddleware>() .UseStageMarker(PipelineStage.Authenticate) // other middlewares omitted for brevity .UseStageMarker(PipelineStage.PreHandlerExecute) .UseWebApi(config);
I will still award the bounty to anyone who comes up with (bounty expired...) I'm still looking for a better solution, which, for example, breaks when an unhandled exception is thrown. (This approach makes VS break when I rethrow the exception in the handler, but the original call stack is lost; I have to set a breakpoint at the faulting line and debug again to be able to intercept the state when an exception is thrown.)
这篇关于在 ASP.NET Web API 2 中禁用*所有*异常处理(为我自己腾出空间)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!