SignalR + 通过操作方法向 Hub 发布消息

Posted

技术标签:

【中文标题】SignalR + 通过操作方法向 Hub 发布消息【英文标题】:SignalR + posting a message to a Hub via an action method 【发布时间】:2011-11-24 20:07:01 【问题描述】:

我正在使用 SignalR (https://github.com/SignalR/SignalR) 的 hub- 功能向所有订阅的客户端发布消息:

public class NewsFeedHub : Hub
public void Send(string channel, string content)

    Clients[channel].addMessage(content);

这在通过 javascript 调用“发送”时工作正常,但我也希望 Web 应用程序发布消息(从 ASP.NET MVC 操作方法中)。我已经尝试实例化一个新对象 ob NewsFeedHub 并调用 Send 方法,但这会导致错误(因为未设置集线器的底层“连接”)。有没有办法在没有连接的情况下使用集线器?

【问题讨论】:

你找到答案了吗?我处于同样的困境。 Signalr dll 似乎初始化了集线器对象,所以我想知道是否需要访问它。 【参考方案1】:

请注意,自提出此问题以来,SignalR API 已多次更改。有些答案可能会过时。这并不意味着它们应该被否决,因为它们在撰写本文时是正确的

对此有另一个更新的答案,如SignalR Wiki中所示

c#

Public ActionResult MyControllerMethod()

    var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
    context.Clients.All.methodInJavascript("hello world");
    // or
    context.Clients.Group("groupname").methodInJavascript("hello world");

vb.net

Public Function MyControllerMethod() As ActionResult
    Dim context = GlobalHost.ConnectionManager.GetHubContext(Of MyHub)()
    context.Clients.All.methodInJavascript("hello world")
    '' or
    context.Clients.Group("groupname").methodInJavascript("hello world")
End Function

更新

此代码已更新。关注http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server 进行更改。

如果您使用的是 DI 容器

如果您使用 DI 容器连接集线器,请从您的容器中获取 IConnectionManager,然后在该 connectionManager 上调用 GetHubContext

【讨论】:

效果很好! :) 解决了我的问题。我在这里得到的一段代码遇到了严重的问题:xhroot.com/blog/2012/07/12/live-updates-using-signalr 这并不意味着它们应该被否决,因为它们在撰写本文时是正确的?如果他们不再正确,他们肯定应该被否决吗? 理想情况下,标签应该是特定于历史准确性的版本。【参考方案2】:

2018 年 2 月,简短而简单的解决方案

为了解决这个问题,我通常按以下方式设计集线器:

public class NewsFeedHub : Hub 

    private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<NewsFeedHub>();

    // Call this from JS: hub.client.send(channel, content)
    public void Send(string channel, string content)
    
        Clients.Group(channel).addMessage(content);
    

    // Call this from C#: NewsFeedHub.Static_Send(channel, content)
    public static void Static_Send(string channel, string content)
    
        hubContext.Clients.Group(channel).addMessage(content);
    


所以很容易从 Javascript 和后端代码调用。这两种方法在客户端产生相同的事件。

【讨论】:

2016 年 8 月,我唯一需要做的改变是:Clients[channel] -> Clients.Group(channel) 这是我遇到过的最好和最有效的解决方案,它是可重用的代码,并且运行良好。 静态方法对我不起作用。我调试并单步执行它,它尝试发送 JavaScript,但没有任何反应。但是,常规的非静态方法可以工作,但做的事情完全相同。问题是我无法从 Web API 调用它,这是我正在尝试做的事情。想法?编辑:这可能很可怕,但是在我的非静态方法中,我将“客户端”保存到私有静态变量(IHubConnectionContext)中,然后在我的静态方法中调用它。有效,但是……感觉不对。 这对我也有用。此外,该方法的静态版本有什么特别的原因吗?我需要从服务层访问集线器,因此我通过 IoC 获得了 IProgressUpdateHub 的一个实例。我无法在接口上实现静态方法,所以我只是将非静态方法更改为调用上下文的 Clients 集合。 这样的问题是你不再可以访问实例变量,说一些跟踪 connectionIds 和用户 ID 的东西。【参考方案3】:

2012 年更新这个答案也很旧! SignalR 的公共 API 似乎在不断变化。自 2012 年 7 月起,Tim B James 拥有新的、正确的 API 用法。

2019 年更新不要再使用这个答案了。在 AspNetCore 上工作的 SignalR 的新版本应参考 Tim B James 接受的答案,或其他投票赞成的答案。为了历史,我把这个答案留在这里。


Mike 当前接受的答案是旧的,不再适用于最新版本的 SignalR。

这是一个更新版本,展示了如何从 MVC 控制器操作向集线器发布消息:

public ActionResult MyControllerMethod() 

     // Important: .Resolve is an extension method inside SignalR.Infrastructure namespace.
     var connectionManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
     var clients = connectionManager.GetClients<MyHub>();

     // Broadcast to all clients.
     clients.MethodOnTheJavascript("Good news!");

     // Broadcast only to clients in a group.
     clients["someGroupName"].MethodOnTheJavascript("Hello, some group!");

     // Broadcast only to a particular client.
     clients["someConnectionId"].MethodOnTheJavascript("Hello, particular client!");
  

【讨论】:

作为对尝试此操作的任何人的说明,这些方法似乎不再可用。向蒂姆 +1 以获取有关新方式的更新。看起来这很不稳定。 我很困惑。 AspNetHost 不是 0.4 的一部分,也不是当前版本(当前为 0.5.2)。这对我不起作用 您是否阅读了顶部的更新?此 API 也已被弃用。请阅读 Tim B James 的回答。【参考方案4】:

我正在寻找.NET Core 解决方案,Google 将我发送到这里,似乎没有人提到.NET Core 解决方案,所以你去吧:

中心,这里没什么特别的:

public class MyHub : Hub

    // ...

在你的controller

public class HomeController : Controller

    readonly IHubContext<MyHub> _hub;

    public HomeController(IHubContext<MyHub> hub)
    
        _hub = hub;
    

    public IActionResult Index()
    
        // ...
        await _hub.Clients.All.SendAsync("ReceiveMessage", "Hello from HomeController");
        // ...
    

【讨论】:

截至 2021 年 6 月,这是唯一有效的代码。让我们希望 API 会保留一段时间。谢谢迈赫迪【参考方案5】:

根据 DDan 的回答,您可以创建一个重载的静态方法并将通用逻辑保留在一个方法中 - 我使用这种模式

public class ChatHub : Hub

    private static IHubConnectionContext<dynamic> GetClients(ChatHub chatHub)
    
        if (chatHub == null)
            return GlobalHost.ConnectionManager.GetHubContext<ChatHub>().Clients;
        else
            return chatHub.Clients;
    

    // Call from JavaScript
    public void Send(string message)
    
        Send(message, this);
    

    // Call from C# eg: Hubs.ChatHub.Send(message, null);
    public static void Send(string message, ChatHub chatHub)
    
        IHubConnectionContext<dynamic> clients = GetClients(chatHub);
        // common logic goes here 
        clients.All.receiveSend(message);
    

然后你可以从你的控制器中使用

Hubs.ChatHub.Send(arg1, null);

【讨论】:

以上是关于SignalR + 通过操作方法向 Hub 发布消息的主要内容,如果未能解决你的问题,请参考以下文章

Asp.Net SignalR Hub类中的操作详解

从 Controller 调用 SignalR Core Hub 方法

SignalR - 从服务器端代码调用 Hub 类方法(存在于单独的 MVC 项目中)

使用 signalr 通知除 self 之外的所有客户端

JS 客户端不调用 SignalR Hub 上的方法

如何在 SignalR 客户端中使用 async/await 和 hub.On