15

I have a lot of code that depends on HttpContext.Current, and I noticed that requests that come from SignalR hubs have HttpContext.Current == null, so my code breaks, for example:

HttpContext.Current.Request.IsAuthenticated

So I came up with following:

public static class UnifiedHttpContext
    {
        private static HubCallerContext SignalRContext { get; set; }

        private static int SignalRUserId 
        {
            get { return WebSecurity.GetUserId(SignalRContext.User.Identity.Name); }
        }

        private static bool IsSignalRRequest
        {
            get { return SignalRContext != null; }
        }

        public static void SetSignalRContext(HubCallerContext context)
        {
            SignalRContext = context;
        }

        public static bool IsAuthenticated
        {
            get
            {
                if (!IsSignalRRequest)
                {
                    return System.Web.HttpContext.Current.Request.IsAuthenticated;
                }
                else
                {
                    return SignalRContext.User.Identity.IsAuthenticated;
                }
            }
        }

        public static int UserId
        {
            get
            {
               if (!IsSignalRRequest)
               {
                   return WebSecurity.CurrentUserId;
               }
               else
               {
                   return SignalRUserId;
               }
            }
        }
    }

And in master hub (every other hub inherits from it):

public abstract class MainHub : Hub
{
        public override Task OnConnected()
        {
            UnifiedHttpContext.SetSignalRContext(Context);
            Groups.Add(Context.ConnectionId, UnifiedHttpContext.UserId.ToString());
            return base.OnConnected();
        }
}
  • Is this correct approach, or is this solved somehow already that I'm not aware of?

  • Is this dangerous since static classes are shared in application, would this set same context for all users? If so can I make it per request?

formatc
  • 4,261
  • 7
  • 43
  • 81
  • "Is this dangerous since static classes are shared in application, would this set same context for all users?" Yes. "If so can I make it per request?" Yes. I would advise against a singleton for your UnifiedHttpContext abstraction. How to best avoid using a signleton depends on where in your code you plan on using your UnifiedHttpContext. – halter73 Feb 10 '14 at 00:18
  • @halter73 Can you elaborate on making it per request? Most implementations that I saw on "per request" use `HttpContext.Items` but in this case I can't because it's null. – formatc Feb 10 '14 at 00:23
  • SignalR does not provide an analog to HttpContext.Items. One possibility might be storing non-static UnifiedHttpContexts in a static Concurrent dictionary keyed off of your UserIds. Once again, without seeing how you are using the UnifiedHttpContext outside of OnConnected, it's hard to know what you are really trying to achieve and the best way to achieve it. – halter73 Feb 10 '14 at 00:35
  • @halter73 For now I am using it along with automapper (mapping is defined once on app start), then calling .Map sometimes from normal postback requests, sometimes from signalR, this is best example of this requirement, other uses are getting user profile, user id to validate allowed CRUD, some helpers - http or https connection.. etc – formatc Feb 10 '14 at 00:44

2 Answers2

19

SignalR gives you access to HubCallerContex instead of HttpContext. You can access HubCallerContext object by using keyword context. If you want to access HttpContext you can fetch it from the context as follows:

System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();

Hope this helps.

Shashank Chaturvedi
  • 2,756
  • 19
  • 29
  • 4
    Just to point out, "context" is not a keyword.. "Context" is the name of a property on Hub of type HubCallerContext. – kmkemp Oct 13 '15 at 19:56
6

Just spent a couple of hours on this problem.

It looks like there is no way to get SignalR context outside of hub class in the same manner like you would do this with HttpContext:

var identity = HttpContext.Current.User.Identity;

or inside Ninject context:

HttpContextBase httpContext = context.Kernel.Get<HttpContextBase>();
var identity = httpContext.Current.User.Identity;

At least the answer to this question states so: Get SignalR User (Hub.Context) outside of hub.

But in this particular case when you need to get current user identity there is a solution which works in both worlds: WebApi/MVC and SignalR.

IPrincipal principal = Thread.CurrentPrincipal;
var identity = principal.Identity;

I used this to get user identity inside my dependency injection code.

Morover, Context property is only available in hub methods (it is null inside hub constructor). But Thread.CurrentPrincipal.Identity will give you the user identity even inside hub constructor.

Community
  • 1
  • 1
user1921819
  • 3,290
  • 2
  • 22
  • 28
  • Thanks, that was useful. I successfully used this approach to access ClaimsPrincipal.Current.Claims outside hub context. – Tushar Kesare Aug 13 '15 at 13:04