从中心外部发送消息

SignalR 中心是用于向连接到 SignalR 服务器的客户端发送消息的核心抽象。 你也可以使用 IHubContext 服务从应用中的其他位置发送消息。 本文介绍如何访问 SignalRIHubContext 以从中心外部向客户端发送通知。

注意

IHubContext 用于将通知发送到客户端,而非用于调用 Hub 上的方法。

查看或下载示例代码(如何下载)

获取 IHubContext 实例

在 ASP.NET Core SignalR 中,你可以通过依赖项注入来访问 IHubContext 实例。 你可以将 IHubContext 实例注入控制器、中间件或其他 DI 服务。 使用该实例向客户端发送消息。

在控制器中注入 IHubContext 实例

通过将 IHubContext 实例添加到构造函数,可以将其注入控制器:

public class HomeController : Controller
{
    private readonly IHubContext<NotificationHub> _hubContext;

    public HomeController(IHubContext<NotificationHub> hubContext)
    {
        _hubContext = hubContext;
    }
}

获权访问 IHubContext 实例后,就像在中心本身一样调用客户端方法:

public async Task<IActionResult> Index()
{
    await _hubContext.Clients.All.SendAsync("Notify", $"Home page loaded at: {DateTime.Now}");
    return View();
}

在中间件中获取 IHubContext 实例

访问中间件管道中的 IHubContext,如下所示:

app.Use(async (context, next) =>
{
    var hubContext = context.RequestServices
                            .GetRequiredService<IHubContext<ChatHub>>();
    //...
    
    if (next != null)
    {
        await next.Invoke();
    }
});

注意

当从 Hub 类外部调用客户端方法时,没有与该调用关联的调用方。 因此,无法访问 ConnectionIdCallerOthers 属性。

需要将用户映射到连接 ID 并保留该映射的应用可以执行以下操作之一:

  • 将单个或多个连接的映射保留为组。 有关详细信息,请参阅SignalR 中的组
  • 通过单一实例服务保留连接和用户信息。 有关详细信息,请参阅将服务注入中心。 单一实例服务可以使用任何存储方法,例如:
    • 字典中的内存中存储。
    • 永久性外部存储。 例如,使用 Azure.Data.Tables NuGet 包的数据库或 Azure 表存储。
  • 在客户端之间传递连接 ID。

从 IHost 获取 IHubContext 实例

从 Web 主机访问 IHubContext 对于与 ASP.NET Core 之外的区域集成很有用,例如,使用第三方依赖项注入框架:

    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();
            var hubContext = host.Services.GetService(typeof(IHubContext<ChatHub>));
            host.Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder => {
                    webBuilder.UseStartup<Startup>();
                });
    }

注入强类型 HubContext

若要注入强类型 HubContext,请确保中心继承自 Hub<T>。 使用 IHubContext<THub, T> 接口而不是 IHubContext<THub> 进行注入。

public class ChatController : Controller
{
    public IHubContext<ChatHub, IChatClient> _strongChatHubContext { get; }

    public ChatController(IHubContext<ChatHub, IChatClient> chatHubContext)
    {
        _strongChatHubContext = chatHubContext;
    }

    public async Task SendMessage(string user, string message)
    {
        await _strongChatHubContext.Clients.All.ReceiveMessage(user, message);
    }
}

有关详细信息,请参阅强类型中心

在泛型代码中使用 IHubContext

注入的 IHubContext<THub> 实例可以强制转换为 IHubContext,而无需指定泛型 Hub 类型。

class MyHub : Hub
{ }

class MyOtherHub : Hub
{ }

app.Use(async (context, next) =>
{
    var myHubContext = context.RequestServices
                            .GetRequiredService<IHubContext<MyHub>>();
    var myOtherHubContext = context.RequestServices
                            .GetRequiredService<IHubContext<MyOtherHub>>();
    await CommonHubContextMethod((IHubContext)myHubContext);
    await CommonHubContextMethod((IHubContext)myOtherHubContext);

    await next.Invoke();
}

async Task CommonHubContextMethod(IHubContext context)
{
    await context.Clients.All.SendAsync("clientMethod", new Args());
}

此操作在以下情况下十分有用:

  • 编写不引用应用正在使用的特定 Hub 类型的库。
  • 编写可应用于多个不同 Hub 实现的泛型代码

其他资源