使用 autofac 在业务逻辑层中从信号器请求中提供用户信息
Provide user information from signalr request in business logic layer using autofac
我有一个 ASP.NET MVC 5 应用程序,带有 SignalR 2 集线器并使用 autofac 作为 DI。
整个业务逻辑都封装在管理器 classes 中它们自己的层中。一些管理器方法需要有关当前登录用户的信息(UserId、TenantId、..)。
我通过向每个需要用户信息的管理器 class 注入一个 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 hub 中有了一个使用相同机制的新方法。
[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 请求没有会话。因此,我还在 hub 方法中将所需的用户信息设置为来自客户端的参数 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种方法,以后还会有更多。
这里是问题的草图
这是一个可能的解决方案。但是很糟糕
SignalR 默认不支持 Session,您应该避免使用它。参见 No access to the Session information through SignalR Hub. Is my design is wrong?。但是您仍然可以使用 cookie 或 querystring 来获取所需的值。
在这两种情况下,您都需要能够访问底层集线器的 HubCallerContext
,可以通过 Hub
的 Context
属性 访问的集线器。
用一个理想的词来说,你应该只需要依赖 SignalAuthorizationProvider
即:
public class SignalrAuthorizationProvider {
public SignalrAuthorizationProvider(HubCallerContext context){
this._context = context;
}
private readonly HubCallerContext _context;
public long? GetUserId() {
return this._context.Request.QueryString["UserId"]
}
}
但由于 SignalR 的设计,这是不可能的。 Context
属性 是在 Hub 构建之后分配的,AFAIK 无法更改它。
源代码在这里:HubDispatcher.cs
一种可能的解决方案是在 Hub
中注入一个可变依赖项,并在 OnConnected
、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
class 这不是一个完美的解决方案。您可以更改 RegisterHubs
autofac 方法以将 AOP 与 Castle.Core
一起使用,并让拦截器为您调用这些方法。
我有一个 ASP.NET MVC 5 应用程序,带有 SignalR 2 集线器并使用 autofac 作为 DI。
整个业务逻辑都封装在管理器 classes 中它们自己的层中。一些管理器方法需要有关当前登录用户的信息(UserId、TenantId、..)。
我通过向每个需要用户信息的管理器 class 注入一个 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 hub 中有了一个使用相同机制的新方法。
[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 请求没有会话。因此,我还在 hub 方法中将所需的用户信息设置为来自客户端的参数 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种方法,以后还会有更多。
这里是问题的草图
这是一个可能的解决方案。但是很糟糕
SignalR 默认不支持 Session,您应该避免使用它。参见 No access to the Session information through SignalR Hub. Is my design is wrong?。但是您仍然可以使用 cookie 或 querystring 来获取所需的值。
在这两种情况下,您都需要能够访问底层集线器的 HubCallerContext
,可以通过 Hub
的 Context
属性 访问的集线器。
用一个理想的词来说,你应该只需要依赖 SignalAuthorizationProvider
即:
public class SignalrAuthorizationProvider {
public SignalrAuthorizationProvider(HubCallerContext context){
this._context = context;
}
private readonly HubCallerContext _context;
public long? GetUserId() {
return this._context.Request.QueryString["UserId"]
}
}
但由于 SignalR 的设计,这是不可能的。 Context
属性 是在 Hub 构建之后分配的,AFAIK 无法更改它。
源代码在这里:HubDispatcher.cs
一种可能的解决方案是在 Hub
中注入一个可变依赖项,并在 OnConnected
、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
class 这不是一个完美的解决方案。您可以更改 RegisterHubs
autofac 方法以将 AOP 与 Castle.Core
一起使用,并让拦截器为您调用这些方法。