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

Posted

技术标签:

【中文标题】OWIN / Katana 的未处理异常全局处理程序?【英文标题】:Unhandled Exception Global Handler for OWIN / Katana? 【发布时间】:2015-06-18 14:46:06 【问题描述】:

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

在作为 Azure 云服务(辅助角色)运行的自托管 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

我还尝试将 wireup 和处理程序移动到 Worker Role OnStart 方法,但仍然没有命中断点。

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

在 .NET Framework 4.5.2 VS 2013 上运行。

感谢所有想法。 谢谢。

【问题讨论】:

【参考方案1】:

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

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
    

【讨论】:

那行不通。默认情况下会处理 Web API 异常。代码不会进入 catch 块。试试看。 @Dr Schizo:如果 web api 捕获到异常并且swallowed (这意味着 web api 已经完成了恢复)。这段代码肯定不会进入 catch 块,就像任何正常的异常处理代码一样。如果您在堆栈跟踪中捕获异常并且不重新抛出它,则堆栈跟踪上方的代码将不会捕获任何异常。这是预期的行为。 @Dr Schizo: Web API exceptions are handled by default => 这意味着异常是handled,因此它不会向上传播堆栈跟踪。这个全局中间件只处理 unhandled 异常,这很有意义,它不处理 handled 异常(通过 web api)。对于任何未处理(由其他中间件)抛出或在 web api 中重新抛出的异常,这个中间件应该能够捕获它们。 我的假设是,如果我有一个控制器在调用它的动作时抛出异常,我希望中间件(你已经描述过)捕捉到这个并且实现做它需要做的任何事情,即记录它等。麻烦的是中间件实际上并没有进入catch块,因此不知道引发的异常也不存在于字典中。我误会了吗? @Khanh 在我启动并运行的基本示例中,我有一个控制器和一个引发异常的单个操作。除了我认为可以处理此问题的默认 Web API 行为外,其他任何地方都无法处理此问题。因此,中间件不做任何事情。您必须替换现有的全局异常处理程序 Web API,然后此中间件才能工作。请参阅我链接到的线程。【参考方案2】:

试试这个:

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

【讨论】:

这应该是答案。【参考方案3】:

@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 的答案中使用代码。相反,我在我的 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;

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

【讨论】:

【参考方案4】:

您可以使用自定义异常过滤器属性。

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)
    
    

【讨论】:

以上是关于OWIN / Katana 的未处理异常全局处理程序?的主要内容,如果未能解决你的问题,请参考以下文章

The Open Web Interface for .NET (OWIN) 源码阅读

OWIN与Katana

Kotlin 协程协程异常处理 ④ ( Android 协程中出现异常导致应用崩溃 | Android 协程中使用协程异常处理器捕获异常 | Android 全局异常处理器 )

Kotlin 协程协程异常处理 ④ ( Android 协程中出现异常导致应用崩溃 | Android 协程中使用协程异常处理器捕获异常 | Android 全局异常处理器 )

Owin/Katana - 与 ASP.NET 请求生命周期集成

身份验证/授权 MVC 5 和 Web API - Katana/Owin