Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SignalR, Owin and exception handling

I've developed a sample SignalR application based on ASP.NET 4.5 & Owin, and I've hosted that app on IIS 7.5.

Everything is working fine, but how can I handle exceptions in Owin?

Consider the following code:

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

This exception won't call Application_Error (and this is my problem).

Where can I get all exceptions from Owin for logging and debugging purposes similarly to Application_Error?

I'm not interested in something like this:

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

This is totally useless for advanced scenarios, something like ASP.NET Web API and ASP.NET MVC.

Action filters with OnException method for override purposes is much better.

like image 328
Yaser Moradi Avatar asked Feb 15 '14 10:02

Yaser Moradi


1 Answers

If you want exception handling specifically for SignalR Hubs, OWIN middleware is not the way to go.

To illustrate just one reason why, suppose that SignalR is using its WebSocket transport when an exception is thrown from inside a Hub method. In this case, SignalR will not close the WebSocket connection. Instead SignalR will write a JSON encoded message directly to the socket to indicate to the client that an exception was thrown. There is no easy way using OWIN middleware to trigger any sort of event when this happens outside of possibly wrapping the entire OWIN WebSocket Extension which I would strongly advise against.

Fortunately SignalR provides its own Hub Pipeline which is perfectly suited for your scenario.

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

You can use the ExceptionContext for more than just logging. For example you can set ExceptionContext.Error to a different exception which will change the exception the client receives.

You can even suppress the exception by setting ExceptionContext.Error to null or by setting ExceptonContext.Result. If you do this, It will appear to the client that the Hub method returned the value you found in ExceptonContext.Result instead of throwing.

A while back a wrote another SO answer about how you can call a single client callback for every exception thrown by a Hub method: SignalR exception logging?

There is also MSDN documentation for HubPipelineModules: http://msdn.microsoft.com/en-us/library/microsoft.aspnet.signalr.hubs.hubpipelinemodule(v=vs.118).aspx

like image 190
halter73 Avatar answered Oct 02 '22 19:10

halter73