使用autofac在业务逻辑层中根据信号请求提供用户信息

时间:2019-01-08 10:44:19

标签: c# asp.net-mvc signalr autofac

我有一个带SignalR 2集线器并为DI使用autofac的ASP.NET MVC 5应用程序。

整个业务逻辑封装在各自层的管理器类中。一些管理器方法需要有关当前登录用户的信息(UserId,TenantId,..)。

我通过向每个需要用户信息的管理器类中注入一个AuthorizationProvider来解决了这个问题。

public interface IAuthorizationProvider 
{
    long? GetUserId();
    long? GteTenantId();
}

public class MyManager : IMyManager
{
    private IAuthorizationProvider _authorizationProvider;

    public MyManager(IAuthorizationProvider authorizationProvider)
    { 
        _authorizationProvider = authorizationProvider;
    }

    public void MyMethod()
    {
        // Getting the User information here is pretty simple
        long userId = _authorizationProvider.GetUserId();
    }
}

通常,我可以从HttpContext和会话中获取用户信息。所以我写了一个SessionAuthorizationProvider:

public class SessionAuthorizationProvider{
    public long? GetUserId()
    {
        HttpContext.Current?.Session?[SessionKeys.User]?.Id;
    }

    public long? GteTenantId() { ... }
}

但是现在我在SignalR集线器中有了一个使用相同机制的新方法。

   [HubName("myHub")]
   public class MyHub : Hub
   {
      private IMyManager _myManager;

      public MyHub(IMyManager myManager)
      { 
          _myManager = myManager;
      }

      [HubMethodName("myHubMethod")]
      public void MyHubMethod(long userId, long tenantId)
      {
          _myManager.MyMethod();
      }
   }

问题是SignalR请求没有会话。因此,我还在集线器方法中将所需的用户信息设置为来自客户端的参数postet。

因此,我认为这是针对SignalR编写新的AuthorizationProvider并适应依赖性解析器的最佳解决方案。但是我无法在新的SignalrAuthorizationProvider中获得当前用户。

public class SignalrAuthorizationProvider{
    public long? GetUserId()
    {
        // How to get the user information here???
    }

    public long? GteTenantId() { /* and here??? */ }
}

是否有针对此问题的推荐解决方案?

当然,我可以扩展MyMethod来接受用户信息作为参数。但是MyMethod从另一个管理器调用另一个方法,并且该管理器也调用另一个方法。仅在最后一次方法调用时才需要用户信息。因此,将来我必须至少更改3种方法,并且还要更改更多方法。

这是问题的草图

Here is a sketch of the problem

这是一个潜在的解决方案。但这很糟糕

This is a potential solution. But it's very bad

1 个答案:

答案 0 :(得分:2)

默认情况下,SignalR不支持

Session,您应避免使用它。参见No access to the Session information through SignalR Hub. Is my design is wrong?。但是您仍然可以使用cookie或querystring获得所需的值。

在两种情况下,您都需要访问基础集线器的HubCallerContext,该集线器可通过Context的{​​{1}}属性进行访问。

用一个理想的词来说,您应该只需要依赖Hub

即:

SignalAuthorizationProvider

但是由于SignalR设计,这是不可能的。在构建集线器和AFAIK之后分配public class SignalrAuthorizationProvider { public SignalrAuthorizationProvider(HubCallerContext context){ this._context = context; } private readonly HubCallerContext _context; public long? GetUserId() { return this._context.Request.QueryString["UserId"] } } 属性,无法对其进行更改。

enter image description here

此处的源代码:HubDispatcher.cs

一种可能的解决方案是在Context内注入可变的依赖关系,并在HubOnConnected方法中更改对象。

OnReconnected

和中心

public class SignalrAuthorizationProvider : IAuthorizationProvider
{
    private Boolean _isInitialized;
    private String _userId;
    public String UserId
    {
        get
        {
            if (!_isInitialized)
            {
                throw new Exception("SignalR hack not initialized");
            }
            return this._userId;
        }
    }

    public void OnConnected(HubCallerContext context)
    {
        this.Initialize(context);
    }
    public void OnReconnected(HubCallerContext context)
    {
        this.Initialize(context); 
    }

    private void Initialize(HubCallerContext context) {
        this._userId = context.QueryString["UserId"];
        this._isInitialized = true;
    }
}

具有可变的依赖关系不是最佳的设计,但是我看不到任何其他方式可以访问public abstract class CustomHub : Hub { public CustomHub(IAuthorizationProvider authorizationProvider) { this._authorizationProvider = authorizationProvider; } private readonly IAuthorizationProvider _authorizationProvider; public override Task OnConnected() { this._authorizationProvider.OnConnected(this.Context); return base.OnConnected(); } public override Task OnReconnected() { this._authorizationProvider.OnReconnected(this.Context); return base.OnReconnected(); } } IRequest

不是拥有一个抽象的HubCallerContext类,这并不是一个完美的解决方案。您可以将Hub autofac方法更改为与RegisterHubs一起使用AOP,然后让拦截器为您调用这些方法。