有一个多租户 SaaS 应用程序,我想在 SignalR Hub 的
tenantId
/OnConnectedAsync
挂钩中为每个 OnDisconnectedAsync
创建组。
问题是
ITenancyContext<ApplicationTenant>
被注册为范围服务,这意味着它仅在请求范围内可用。在 SignalR hub 的上下文中,它与请求无关,因此不可用。
那么我该如何让它可用呢?授权它并以某种方式丰富声明?
public sealed class NotificationHub : Hub
{
readonly ILogger<NotificationHub> _logger;
readonly Guid _tenantId;
public NotificationHub(ILogger<NotificationHub> logger, ITenancyContext<ApplicationTenant> tenancyContext) => (_logger, _tenantId) = (logger, tenancyContext.Tenant.Id);
public override async Task OnConnectedAsync()
{
await JoinGroup(_tenantId.ToString());
_logger.LogInformation("{ConnectionId} has connected to the hub. TenantId: {TenantId}", Context.ConnectionId, _tenantId);
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
await LeaveGroup(_tenantId.ToString());
_logger.LogInformation("{ConnectionId} was disconnected from the hub. TenantId: {TenantId}", Context.ConnectionId, _tenantId);
await base.OnDisconnectedAsync(exception);
}
Task JoinGroup(string groupName) => Groups.AddToGroupAsync(Context.ConnectionId, groupName);
Task LeaveGroup(string groupName) => Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
}
我认为我们可以创建一个中间件来实现它。在这个中间件中注入
ITenancyContext<ApplicationTenant>
,我们称之为TenantMiddleware
.
public class TenantMiddleware
{
private readonly ITenancyContext<ApplicationTenant> _tenancyContext;
public TenantMiddleware(ITenancyContext<ApplicationTenant> tenancyContext)
{
_tenancyContext = tenancyContext;
}
public async Task InvokeAsync(HttpContext context, Func<Task> next)
{
var user = context.User.Identity as ClaimsIdentity;
if (user != null)
{
var TenantId= _tenancyContext.Tenant.Id;
user.AddClaim(new Claim("TenantId", TenantId.ToString()));
}
await next();
}
}
然后我们可以在您的
NotificationHub
课上使用它。
public override async Task OnConnectedAsync()
{
// Get TenantId like below.
var user = Context.User.Identity as ClaimsIdentity;
var tenantIdClaim = user?.FindFirst("TenantId");
_tenantId = tenantIdClaim != null ? Guid.Parse(tenantIdClaim.Value) : Guid.Empty;
...
await JoinGroup(_tenantId.ToString());
_logger.LogInformation("{ConnectionId} has connected to the hub. TenantId: {TenantId}", Context.ConnectionId, _tenantId);
await base.OnConnectedAsync();
}