带有 SignalR 集线器的 ASP.NET Core 中的范围服务

Posted

技术标签:

【中文标题】带有 SignalR 集线器的 ASP.NET Core 中的范围服务【英文标题】:Scoped services in ASP.NET Core with SignalR Hubs 【发布时间】:2019-11-09 14:04:45 【问题描述】:

我已将 SignalR 引入到我的 ASP.NET Core 2 项目中,但在使用我通常在控制器中使用的 scoped 服务时遇到了一些问题。我觉得问题可能是由于 HTTP 请求、websocket 和集线器之间的生命周期不同。

对于每个 HTTP 请求,中间件都会读取 Authorization 令牌并更新请求的范围服务 (IUser) 上的一些属性(例如 id、声明等)。我在所有控制器中都使用此服务,没有任何问题。为了让它与 SignalR 一起使用,我发送了一个 access_token 查询参数,并预先使用了其他一些 middleware 将此查询参数添加为可以正常工作的标头。

当我尝试访问 SignalR 集线器中的 IUser 服务时,就会出现问题。在构建集线器时,注入的IUser 没有设置任何属性,尽管/hub 请求的中间件只是设置了它们。

如果我将服务设置为单例,那么它可以工作,但IUser 的持续时间不应超过单个请求。

我应该如何为特定 SignalR 连接设置IUser

// Startup.cs - User has several settable properties 
services.AddScoped<IUser, User>();
// User Middleware
public class UserMiddleware

    private readonly RequestDelegate _next;

    public UserMiddleware(RequestDelegate next)
    
        _next = next;
    

    public Task Invoke(HttpContext context)
    
        // retrieve service for this request
        var user = context.RequestServices.GetService<IUser>();

        // set some properties on the User from auth token
        // e.g. User.Id = 1;

        return _next(context);
    

[Authorize(Roles = Claim.Names.Read)]
public class MyHub : Hub

    private readonly IUser _user;

    public MyHub(IUser user)
    
        // user that gets injected has null properties
        _user = user;
    

    public async Task Foo()
    
        // do work with the _user credentials
    

public class DownloadController : Controller

    private readonly IUser _user;

    public DownloadController(IUser user)
    
        // user gets injected and has properties set
        _user = user;
    

【问题讨论】:

由于该中心旨在与所有注册客户进行交互,而不仅仅是一个特定用户,因此使用某种全局范围的“用户”的想法没有多大意义。您在这里真正想要实现的目标是什么? @ChrisPratt 对,但是每次方法调用都会创建一个新的集线器实例,对吧?我正在尝试在需要经过身份验证的用户的服务器端集线器方法中进行一些调用。我可以让集线器方法将用户作为参数,但我认为使用中间件会更好。 this.Context.User 属性存在于Hub 基类中,所以我不认为用我自己的IUser 类尝试类似的东西是不合理的。 【参考方案1】:

您是否在 Hub 方法中尝试过 Context.GetHttpContext().RequestServices?那应该是正在进行的请求的IServiceProvider

【讨论】:

【参考方案2】:

要在 Signalr Hub 中使用作用域服务,您可以注入 ServiceProvider 并直接在其中创建作用域:

public class ChatHub : Hub
      
    private IServiceProvider _serviceProvider;
    public ChatHub(IServiceProvider serviceProvider)
    
        _serviceProvider= serviceProvider;
    

    public async Task Foo()
    
        using (var scope = _serviceProvider.CreateScope())
        
           var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();

           var user = scope.ServiceProvider.GetRequiredService<IUser>();    

        

    

【讨论】:

问题是我想使用中间件生成的作用域IUser。上面的代码只创建了一个新的IUser,没有设置属性。

以上是关于带有 SignalR 集线器的 ASP.NET Core 中的范围服务的主要内容,如果未能解决你的问题,请参考以下文章