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

本文关键字:全局 处理 程序 异常 未处理 Katana OWIN | 更新日期: 2023-09-27 17:56:53

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

在作为 Azure 云服务(辅助角色)运行的自承载 OWIN/Katana实现中,我将以下代码放在中间件中:

throw new Exception("pooo");

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

 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

我还尝试将线路和处理程序移动到辅助角色 OnStart 方法,但仍然没有命中断点。

我根本没有使用 WebAPI,但确实查看了有关那里所做的事情的帖子,但我发现什么都不清楚,所以我在这里。

在 .NET Framework 4.5.2 VS 2013 上运行。

所有的想法都值得赞赏。谢谢。

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

尝试编写自定义中间件并将其作为第一个中间件:

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
      }
   }
 }

将其作为第一个中间件:

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
    }
}

试试这个:

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());
}

@Khanh TO的答案非常好; 漂亮的简洁代码,解释得很好。

它对我不起作用。 我向我的一个 .NET MVC 控制器添加了一个测试异常,如下所示:

throw new Exception("Test GlobalExceptionMiddleware");

Invoke方法中未捕获异常。

这很容易调试:

  1. 在测试异常上放置断点。
  2. F10 或单步执行每一行,直到到达处理异常的代码。

对我来说,有代码添加到BaseController覆盖OnException中。 此代码正在处理异常,而不是重新引发。 我希望这是对@Khanh TO的回答的有用补充。

更新

好的,我意识到这个问题是用Web API标记的,我的用例是.NET MVC,但我在搜索Owin中间件时发现了这个问题。 当我调试具有测试异常的解决方案并在"局部变量"视图中观察$exception时,当我返回到 Owin 中间件时,异常消失了。 因此,我没有在 @Khanh TO的答案中使用代码。 相反,我在我的Startup和Global.asax中添加了try/catch块。 我也在我的基本控制器中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;
}

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

您可以使用自定义异常筛选器属性。

public static class WebApiConfiguration
{
    public static void Register(HttpConfiguration config)
    {
        config.RegisterExceptionFilters();
    }
}
public static class HttpConfigurationFilterExtensions
{
    public static HttpConfiguration RegisterExceptionFilters(this HttpConfiguration httpConfig)
    {
        httpConfig.Filters.Add(new CustomExceptionFilterAttribute());
        return httpConfig;
    }
}
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext context)
    {
        if (context.Exception is CustomException)
        {
            context.Response = new HttpResponseMessage((HttpStatusCode)CustomHttpStatusCode.CustomCode);
        }
    }
}
public class CustomException : Exception
{
    public CustomException(string message)
        : base(message)
    {
    }
}