【问题标题】:SignalR, Owin and exception handlingSignalR、Owin 和异常处理
【发布时间】:2014-02-15 10:07:08
【问题描述】:

我已经开发了一个基于 ASP.NET 4.5 和 Owin 的 SignalR 示例应用程序,并且我在 IIS 7.5 上托管了该应用程序。

一切正常,但如何在 Owin 中处理异常?

考虑以下代码:

[HubName("SampleHub")]
public class SampleHub : Hub
{
    public SampleHub()
    {
        throw new InvalidOperationException("?!");
    }
}

这个异常不会调用Application_Error(这是我的问题)。

我在哪里可以从 Owin 获取所有异常以进行日志记录和调试,类似于 Application_Error

我对这样的事情不感兴趣:

app.UseErrorPage(new ErrorPageOptions()
{
    ShowCookies = true,
    ShowEnvironment = true,
    ShowExceptionDetails = true,
    ShowHeaders = true,
    ShowQuery = true,
    ShowSourceCode = true
});

这对于高级场景完全没用,比如 ASP.NET Web API 和 ASP.NET MVC。

使用 OnException 方法覆盖的动作过滤器要好得多。

【问题讨论】:

    标签: asp.net signalr owin


    【解决方案1】:

    如果您想要专门针对 SignalR 集线器进行异常处理,OWIN 中间件不是要走的路。

    为了说明其中的一个原因,假设当从 Hub 方法内部抛出异常时 SignalR 正在使用其 WebSocket 传输。在这种情况下,SignalR 不会关闭 WebSocket 连接。相反,SignalR 会将 JSON 编码的消息直接写入套接字,以向客户端指示引发了异常。没有简单的方法使用 OWIN 中间件来触发任何类型的事件,当这种情况发生在可能包装整个 OWIN WebSocket Extension 之外时,我强烈建议不要这样做。

    幸运的是,SignalR 提供了自己的Hub Pipeline,非常适合您的场景。

    using System;
    using System.Diagnostics;
    using Microsoft.AspNet.SignalR.Hubs;
    
    public class MyErrorModule : HubPipelineModule
    {
        protected override void OnIncomingError(ExceptionContext exceptionContext, IHubIncomingInvokerContext invokerContext)
        {
            MethodDescriptor method = invokerContext.MethodDescriptor;
    
            Debug.WriteLine("{0}.{1}({2}) threw the following uncaught exception: {3}",
                method.Hub.Name,
                method.Name,
                String.Join(", ", invokerContext.Args),
                exceptionContext.Error);
        }
    }
    

    您可以将 ExceptionContext 用于日志记录以外的其他用途。例如,您可以将 ExceptionContext.Error 设置为不同的异常,这将更改客户端收到的异常。

    您甚至可以通过将ExceptionContext.Error 设置为null 或设置ExceptiononContext.Result 来抑制异常。如果这样做,客户端会看到 Hub 方法返回了您在 ExceptiononContext.Result 中找到的值,而不是抛出。

    不久前,a 写了另一个关于如何为 Hub 方法引发的每个异常调用单个客户端回调的 SO 答案:SignalR exception logging?

    还有 HubPipelineModules 的 MSDN 文档:http://msdn.microsoft.com/en-us/library/microsoft.aspnet.signalr.hubs.hubpipelinemodule(v=vs.118).aspx

    【讨论】:

    • 非常感谢您的出色解决方案。但是此模块无法记录 SignalR 的“集线器描述符提供程序的异常”和“集线器激活器”部分等异常 signalR 模块不会记录集线器的构造函数异常。您的模块很有用,我将把它用作 SignalR 记录器。但是 SignalR 的创建和启动过程是由 'Owin' 自己执行的。我在哪里可以得到这些例外。暂时,我已经实现了 SignalR 的所有扩展点,例如 IHubDescriptorProvider、IHubActivator 等,以使日志记录成为可能,这在我看来显然是错误的。有什么想法吗?
    • @halter73 它不适用于在其他 HubPipeLineModules 方法或当前模块的其他方法中引发的异常。
    • 感谢您的回答。当我在客户端调用的集线器方法中抛出异常时,我仍然无法调用 OnIncomingError。它通过GlobalHost.HubPipeline.AddModule()注册。为什么我的HubPipelineModule 没有被调用?
    【解决方案2】:

    @halter73 的回答非常适合在集线器内引发的错误,但它不会捕获在创建过程中引发的错误。

    我遇到了异常:

    System.InvalidOperationException: 'foobarhub' Hub 无法解析。

    服务器正在为这个异常返回一个 HTML 页面,但我需要它以 JSON 格式更好地与我的 Angular 应用程序集成,因此基于this answer,我实现了一个OwinMiddleware 来捕获异常并更改输出格式。你可以用它来记录错误。

    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)
            {
                context.Response.ContentType = "application/json";
                context.Response.StatusCode = 500;
    
                await context.Response.WriteAsync(JsonConvert.SerializeObject(ex));
            }
        }
    
    }
    

    OwinStartup.cs添加注册,记得放在MapSignalR方法调用之前:

    public class OwinStartup
    {
        public void Configuration(IAppBuilder app)
        {
            app.Use<GlobalExceptionMiddleware>(); // must come before MapSignalR()
    
            app.MapSignalR();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-10-18
      • 1970-01-01
      • 1970-01-01
      • 2020-07-14
      • 2016-04-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-10
      • 2017-06-02
      相关资源
      最近更新 更多