首页 文章

OWIN / Katana未处理的异常全局处理程序?

提问于
浏览
30

在Katana(OWIN)实现中实现全局异常捕获器处理程序的正确方法是什么?

在作为Azure Cloud 服务(工作者角色)运行的自托管OWIN / Katana实现中,我将此代码放在中间件中:

throw new Exception("pooo");

然后我将此代码放在Startup类Configuration方法中,在事件处理程序中设置断点:

AppDomain.CurrentDomain.UnhandledException += 
    CurrentDomain_UnhandledExceptionEventHandler;

和同一个类中的事件处理程序(在第一行设置断点):

private static void CurrentDomain_UnhandledExceptionEventHandler(object sender, UnhandledExceptionEventArgs e)
{
    var exception = (Exception)e.ExceptionObject;
    Trace.WriteLine(exception.Message);
    Trace.WriteLine(exception.StackTrace);
    Trace.WriteLine(exception.InnerException.Message);
}

代码运行时,不会触发断点 . Visual Studio输出窗口确实包括此:

A first chance exception of type 'System.Exception' occurred in redacted.dll
A first chance exception of type 'System.Exception' occurred in mscorlib.dll

我也尝试将连接和处理程序移动到Worker Role OnStart方法,但仍然没有命中断点 .

我根本没有使用WebAPI,但确实查看过那里做的帖子,但我没有发现任何明确的内容,所以我在这里 .

在.NET Framework 4.5.2,VS 2013上运行 .

所有想法都赞赏 . 谢谢 .

3 回答

  • 0

    尝试编写自定义中间件并将其作为 first 中间件放置:

    public class GlobalExceptionMiddleware : OwinMiddleware
    {
       public GlobalExceptionMiddleware(OwinMiddleware next) : base(next)
       {}
    
       public override async Task Invoke(IOwinContext context)
       {
          try
          {
              await Next.Invoke(context);
          }
          catch(Exception ex)
          {
              // your handling logic
          }
       }
     }
    

    将其作为 first 中间件:

    public class Startup
    {
        public void Configuration(IAppBuilder builder)
        {
            var config = new HttpConfiguration();
    
            builder.Use<GlobalExceptionMiddleware>();
            //register other middlewares
        }
    }
    

    当我们将这个中间件注册为第一个中间件时,其他中间件(在堆栈跟踪中)发生的任何异常都会传播并被此中间件的 try/catch 块捕获 .

    并不是必须始终将其注册为第一个中间件,如果您不需要对某些中间件进行全局异常处理,只需在此之前注册这些中间件即可 .

    public class Startup
    {
        public void Configuration(IAppBuilder builder)
        {
            var config = new HttpConfiguration();
    
            //register middlewares that don't need global exception handling. 
            builder.Use<GlobalExceptionMiddleware>();
            //register other middlewares
        }
    }
    
  • 1

    试试这个:

    public class CustomExceptionHandler : IExceptionHandler
    {    
        public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken)
        {
          // Perform some form of logging
    
          context.Result = new ResponseMessageResult(new HttpResponseMessage
          {
            Content = new StringContent("An unexpected error occurred"),
            StatusCode = HttpStatusCode.InternalServerError
          });
    
          return Task.FromResult(0);
        }
    }
    

    并在启动时:

    public void Configuration(IAppBuilder app)
    {
      var config = new HttpConfiguration();
    
      config.Services.Replace(typeof(IExceptionHandler), new CustomExceptionHandler());
    }
    
  • 38

    @Khanh TO的答案非常好;很好的简洁代码,并得到很好的解释 .

    它不适合我 . 我在我的一个.NET MVC控制器中添加了一个测试异常,如下所示:

    throw new Exception("Test GlobalExceptionMiddleware");
    

    Invoke 方法未捕获该异常 .

    这很容易调试:

    • 在测试异常上放置一个断点 .

    • 点击F10或跳过每一行,直到找到处理异常的代码 .

    对我来说,有一个代码被添加到 BaseController 覆盖 OnException . 此代码处理异常而不是重新抛出 . 我希望这是@Khanh TO答案的有用附录 .

    更新

    好的,我意识到这个问题是用Web API标记的,我的用例是.NET MVC,但我在搜索Owin中间件时发现了这个问题 . 当我使用测试异常调试我的解决方案,并在Locals视图中观察 $exception 时,当我回到Owin中间件时,异常就消失了 . 因此我没有在@Khanh TO的答案中使用该代码 . 相反,我已将try / catch块添加到我的Startup和Global.asax中 . 我在我的基本控制器中也有 OnException ,如下所示:

    protected override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.ExceptionHandled)
        {
            return;
        }
        var e = filterContext.Exception;
        Log.Error("BaseController.OnException caught exception:", e);
        filterContext.ExceptionHandled = true;
    }
    

    此答案将捕获应用程序启动时的任何异常以及控制器中的任何异常 . 这足以满足我的需求 .

相关问题