本文介绍了禁用ASP.NET Web API 2中的* all *异常处理(为我自己腾出空间)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在中间件组件中连接异常处理,如下所示:

public override async Task Invoke(IOwinContext context)
{
    try
    {
        await Next.Invoke(context);
    }
    catch (Exception ex)
    {
        // Log error and return 500 response
    }
}

但是,我想捕获的一些异常在被Web API管道捕获并转换为HttpErrorResponse之前就可以获取.在此过程中,我丢失了许多有关错误的详细信息,因此在进行调试等操作时,我无法获得有用的堆栈跟踪信息(引发异常时调试器甚至都不会停止-我必须手动浏览代码,看看失败了...).

我尝试通过以下实现添加自定义异常处理程序:

public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
{
    var owinContext = context.Request.GetOwinContext();
    owinContext.Set(Constants.ContextKeys.Exception, context.Exception);
    return Task.FromResult(0);
}

在我的启动配置中通过config.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler());注册,但是在通过

context.Get<Exception>(Constants.ContextKeys.Exception);

执行后通过

context.Get<Exception>(Constants.ContextKeys.Exception);

查看

context.Get<Exception>(Constants.ContextKeys.Exception);

still 并没有提供我想要的所有详细信息,也没有提供调试器在故障点停止的信息.

有没有办法完全 关闭所有内置错误处理,以便我自己的中间件可以解决?

澄清,因为很多人似乎误会了我的追求:

  • Web API中的内置错误处理功能捕获一些(但不是全部)异常并将其重写为500个响应.
  • 我想捕获所有 异常,进行一些日志记录,然后然后用我选择的信息发出500个响应 (对于大多数情况,请参阅下一个项目符号.)
  • 还有一些表示业务逻辑故障的异常,我想为其返回40倍的错误.
  • 我希望它位于(app)管道的顶部,即在请求生命周期中包装其他其他
  • 我想使用OWIN来处理此问题,以使其可移植到将来可能出现的自托管方案(即,并非始终将这个应用程序始终托管在IIS上是一成不变的-HTTP模块,Global.asax.cs等)在这里不相关).

解决方案

更新:我为此写了博客.在研究博客文章时,我发现了一些改进的潜力.我已经更新了此答案的相关部分.有关为什么我认为这比这里的所有其他建议或默认行为更好的详细信息,请阅读整篇文章:)


我现在采用了以下方法,即使不是100%符合我的期望,这种方法也可以正常工作.

  • 创建一个类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;
        }
    }
    

  • 让该类替换 Web API的IExceptionHandler服务:

    config.Services.Replace(typeof(IExceptionHandler), new PassthroughExceptionHandler());
    

  • 创建一个我想要做的中间件类:

    public class ExceptionHandlerMiddleware
    {
        public override async Task Invoke(IOwinContext context)
        {
            try
            {
                await Next?.Invoke(context);
            }
            catch (Exception ex)
            {
                // handle and/or log
            }
        }
    }
    

  • 首先在堆栈中注册该中间件:

    app.Use<ExceptionHandlerMiddleware>()
       .UseStageMarker(PipelineStage.Authenticate)
       // other middlewares omitted for brevity
       .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 HttpErrorResponses 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);
    

(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中的* all *异常处理(为我自己腾出空间)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-22 22:35
查看更多