SignalR 使用 GlobalHost.ConnectionManager.GetHubContext 从外部集线器调用客户端方法不起作用。但是从集线器内部调用确实

Posted

技术标签:

【中文标题】SignalR 使用 GlobalHost.ConnectionManager.GetHubContext 从外部集线器调用客户端方法不起作用。但是从集线器内部调用确实【英文标题】:SignalR calling client method from outside hub using GlobalHost.ConnectionManager.GetHubContext doesn't work. But calling from within the hub does 【发布时间】:2013-12-31 21:48:27 【问题描述】:

我正在尝试从 .net Web API 控制器操作中调用客户端方法。

我可以这样做吗?

我能找到的唯一一篇接近我想要做的帖子是这个:

SignalR + posting a message to a Hub via an action method

使用 GlobalHost.ConnectionManager.GetHubContext 从 asp.net MVC 控制器操作中发送一条消息。

当我尝试在我的 Web API 操作中执行此操作时,不会引发任何错误,但从未在客户端调用方法“methodInjavascript”。

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

当我在该操作中设置断点时,我看到代码正在到达并执行。但是,javascript 客户端没有任何反应。

为什么? Web API 在引擎盖下是否如此不同以至于这不起作用?有没有其他人尝试过并且成功了?

当我从集线器“内部”调用“methodInJavascript”时,它运行良好。从 .net Web API 控制器操作中调用时将无法正常工作。

更新:

在研究了这个问题后,我没有解决方案。我只能假设像 Server to client messages not going through with SignalR in ASP.NET MVC 4 和 calling SignalR hub from WebAPI controller issues 这样的示例中缺少一些东西,比如可能有一个额外的配置步骤来启用从 HubContext 或其他东西的调用。我最初在此处发布的代码与这些示例中出现的代码类似,但并未证明存在任何缺陷。任何人都可以看到代码中的缺陷吗?从 html 调用有效。我在我的应用程序中广泛使用它并且从未遇到过问题。我从未在 API 控制器工作中看到来自 HubContext 的调用。没有错误。只是在客户端上没有结果。

已解决(有点):

上面的代码确实像发布时一样工作。虽然不能通过 localhost 在 Visual Studio 开发环境中工作。没有错误,但在客户端没有结果。将代码按原样发布到网络上的真实服务器确实有效。我从没想过会有什么不同,所以我从来没有尝试过。认为如果它在本地不起作用,它将无法发布。它现在可以正常工作,但我想知道为什么它不能在开发环境中通过 localhost 工作。无法使用断点等进行本地测试。

我有一种感觉是信号器虚拟目录。在本地运行与发布时有所不同。不知道是什么,但我看到很多帖子,比如http://www.bitwisejourneys.com/signalr-hosting-in-iis-a-nasty-gotcha/。现在阅读,看看是否有办法让它在本地工作和发布。

【问题讨论】:

我真的完全不明白这一点。我看到了文档。我在很多地方都看到了代码 sn-ps。它们看起来都一样。有没有人真的这样做过?有没有人真正看到过它的工作原理? 是的,我这样做了,它对我也不起作用。我得到与 OP 相同的行为 你有没有深究过这个? @Robert,您关于 VD 的注释为我解决了这个问题。我注意到我引用 /signalr/hubs 的页面正在获取 localhost:1234/signalr/hubs 并且生成的内容正在初始化 /signalr 而不是 /MyWeb/signalr 的路径。但是当我输入 localhost:1234/MyWeb/signalr/hubs - 生成的内容连接 URL 是 /MyWeb/signalr。当您在 IISExpress 中使用 VD 时,它会创建两个网站 - 一个在 / 中,一个在 /MyWeb 中,因此信号器似乎可以工作,但是,我猜您最终会得到两个不同的集线器。确保生成的 /signalr/hub 文件在连接 URL 中包含 VD。 我也面临同样的问题。发布后,运行良好。 【参考方案1】:

几天前我遇到了同样的问题。我花了 2 天时间找到解决方案并解决它。经过认真调查后,问题的根本原因是我自定义设置的信号器依赖解析器。

最后我找到了this link,就是这么说的:

替换 DependencyResolver

您可以更改 DependencyResolver 以使用您的 DI 容器 通过设置 GlobalHost.DependencyResolver 进行选择。

注意:不要覆盖 PreApplicationStart 中的全局解析器,它 不会起作用,或者它只会在某些时候起作用。在里面做 PostApplicationStart(使用 WebActivator)或 Global.asax。

这里重要的地方是 NOTE。当然,在 signalr 2.0 之后,此文档已弃用。所以我在这里混合了一些新的 SignalR API。在新的 SignalR API 中不再使用 WebActivatorEx。首选 OwinStartup 而不是 WebActivator。

[assembly: OwinStartupAttribute(typeof(YourNamespace.Startup))]
namespace YourNamespace

    public partial class Startup
    
        public void Configuration(IAppBuilder app)
        
            //IoC container registration process
            UnityConfig.RegisterComponents();

            UnityConfig.Container.RegisterType<AHub, AHub>();

            HubConfiguration config = new HubConfiguration();
            config.EnableJavaScriptProxies = true;


            //You should remove your dependency resolver code from here to Global.asax Application_Start method. Before setting the MVC properties.
            //config.Resolver = new SignalrDefaultDependencyResolver(UnityConfig.Container); // your dependency resolver
            //


            app.MapSignalR(config);
        
    

在你的 global.asax 中

namespace YourNamespace

    public class MvcApplication : System.Web.HttpApplication
    
        protected void Application_Start()
        
            //Here your SignalR dependency resolver
            GlobalHost.DependencyResolver = new SignalrDefaultDependencyResolver(UnityConfig.Container);


            //other settings goes on
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);

            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        
    

我不想在这里发送所有代码,以显示真正的问题。

所以对我来说,现在一切正常。依赖注入也可以。但糟糕的部分无处不在,我搜索大卫福勒说“这是设计使然”。我开始觉得这个设计真的是必要的还是错误的。

希望它可以帮助其他研究相同问题的人。

【讨论】:

感谢您的回答。然而我不明白。为什么我要替换 DependencyResolver。关于从集线器外部调用客户端方法的文档 (asp.net/signalr/overview/signalr-20/hubs-api/…) 对此只字未提。我上面引用的例子也没有提到它。我上面的代码对我来说是正确的。你觉得它有什么不正确的地方吗? 好的,我的回答是假设您使用的是依赖注入。在这里回答之前,我检查了您的示例链接,并且在您的第二个链接中,OP 正在谈论依赖关系解析。因此,如果您没有使用依赖解析器,请纠正我。这里还有一件事。您在上面的评论中给我的链接也使用依赖解析器。 StockTickerHub 也是单例实例。所以它总是给你同样的东西。但在我们的例子中,依赖解析器给出了不同的实例。如果你愿意,我可以用更多代码更新我的答案。 @Robert 请检查this link,有一个没有nuget依赖dll的示例解决方案 我现在正在查看该链接。我还改写了我上面的问题以试图澄清。 嗯...您的示例解决方案看起来像我的。我们的处理方式基本相同。控制器是一样的。客户端js是一样的。我只是没有从客户端收到消息。必须有一些基本的东西,我不了解集线器。我的印象是只有一个实例。因此,如果我要求一个集线器上下文,它将指向我的应用程序正在运行的一个集线器实例。是否可能有不止一个实例。获取代理是否会创建我的集线器的新实例,让我的客户端 javascript 与其他以前的实例通信?【参考方案2】:

我遇到了同样的问题,它与 IoC 相关(无论是 ninject 还是城堡)。 如果将全局依赖解析器设置为 IoC 管理器,它还将替换 SignalR 内部管道解析。这会使您的 SingleTon 客户端中心无法正常工作。

我只通过 IoC 的服务器集线器解决了这个问题 下面的代码需要SignalHubActivator(可以在网上找到)

现在,GlobalHost.ConnectionManager.GetHubContext 将返回单个实例,并且将再次正确调用客户端方法!

 //configuration.Resolver = signalrDependency ; dont, this will cause GlobalHost.ConnectionManager to be intercepted by Castle


 configuration.Resolver.Register(typeof(IHubActivator),
                                      () => new SignalHubActivator(container));

【讨论】:

以上是关于SignalR 使用 GlobalHost.ConnectionManager.GetHubContext 从外部集线器调用客户端方法不起作用。但是从集线器内部调用确实的主要内容,如果未能解决你的问题,请参考以下文章

SignalR.Owin 与 SignalR.SelfHost

ASP.NET Core的实时库: SignalR简介及使用

[Asp.net 开发系列之SignalR篇]专题二:使用SignalR实现酷炫端对端聊天功能

使用 .NET 客户端时的 SignalR 传输

MVC5使用SignalR进行双向通信

SignalR系列教程:在MVC在使用SignalR