Унифицированный статический класс между HttpContext и SignalR HubCallerContext

У меня много кода, который зависит от HttpContext.Current, и я заметил, что запросы, поступающие из концентраторов SignalR, имеют HttpContext.Current == null, поэтому мой код разбивается, например:

HttpContext.Current.Request.IsAuthenticated

Итак, я придумал следующее:

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

И в мастер-хабе (каждый другой хаб наследует от него):

public abstract class MainHub : Hub
{
        public override Task OnConnected()
        {
            UnifiedHttpContext.SetSignalRContext(Context);
            Groups.Add(Context.ConnectionId, UnifiedHttpContext.UserId.ToString());
            return base.OnConnected();
        }
}
  • Является ли этот правильный подход, или это так или иначе решено, что я не знаю?

  • Является ли это опасным, поскольку статические классы совместно используются в приложении, будет ли этот набор одинаковым контекстом для всех пользователей? Если это так, я могу сделать это по запросу?

Ответы

Ответ 1

SignalR дает вам доступ к HubCallerContex вместо HttpContext. Вы можете получить доступ к объекту HubCallerContext, используя ключевое слово context. Если вы хотите получить доступ к HttpContext, вы можете получить его из контекста следующим образом:

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

Надеюсь, что это поможет.

Ответ 2

Просто потратил пару часов на эту проблему.

Похоже, что невозможно получить контекст SignalR вне класса хаба таким же образом, как вы это сделали бы с HttpContext:

var identity = HttpContext.Current.User.Identity;

или внутри контекста Ninject:

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

По крайней мере, ответ на этот вопрос гласит: Получить SignalR User (Hub.Context) за пределами концентратора.

Но в этом конкретном случае, когда вам нужно получить текущий идентификатор пользователя, есть решение, которое работает в обоих мирах: WebApi/MVC и SignalR.

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

Я использовал это, чтобы получить идентификатор пользователя внутри моего кода инъекции зависимостей.

Morover, свойство Context доступно только в hub-методах (это null внутри конструктора хаба). Но Thread.CurrentPrincipal.Identity даст вам идентификатор пользователя даже внутри конструктора хаба.