使用GlobalHost.ConnectionManager.GetHubContext从hub外部调用客户端方法的SignalR不起作用。但是从hub内部调用可以。

32

我正在尝试从 .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在内部是如此不同以至于这不起作用吗?有人尝试过并成功了吗?
当我从我的Hub“内部”调用“methodInJavascript”时,它完美地工作。只有在从.NET Web API控制器操作中调用时才无法工作。
更新:
经过研究,我没有解决方案。我只能假设像这个ASP.NET MVC 4 中使用 SignalR 时服务器到客户端消息无法传递以及这个从WebAPI控制器调用SignalR Hub问题这样的示例中缺少某些东西,例如启用从HubContext调用的其他配置步骤等。我最初发布的代码与这些示例中的代码类似,并没有被证明存在任何缺陷。从html中调用有效。我在我的应用程序中广泛使用它,并且从未遇到过问题。我从来没有见过来自API控制器的HubContext的调用起作用。没有错误,只是客户端没有结果。
已解决(有点):
上面的代码确实可以直接使用,但只有在发布后才有效。但在Visual Studio开发环境通过localhost不起作用。没有错误,但客户端端没有结果。将代码按原样发布到Web上的真实服务器中确实有效。我从未想过会有区别,所以我从未尝试过。我认为如果在本地无法工作,则不会发布。现在它正在实时工作,但我想知道为什么它在开发环境中的localhost上不起作用。无法使用断点等在本地测试。
我感觉是signalr虚拟目录。当本地运行和发布时,有些不同。不确定是什么,但我看到很多类似的帖子,例如http://www.bitwisejourneys.com/signalr-hosting-in-iis-a-nasty-gotcha/。现在正在阅读,以了解是否有方法使其在本地和发布时都能工作。

2
我真的完全不理解这个。我看到了文档。我在很多地方看到了代码片段。它们看起来都一样。有人真的做过这个吗?有人真的看到它工作了吗? - Robert
1
是的,我也做了这个,但对我也不起作用。我得到与OP相同的行为。 - godseyeview
你最终解决了这个问题吗? - jonho
@Robert,你提到的VD解决了我的问题。我注意到我的页面引用了 /signalr/hubs,但是生成的内容将路径初始化为 /signalr 而不是 /MyWeb/signalr,因此会 GET localhost:1234/signalr/hubs。但是当我输入 localhost:1234/MyWeb/signalr/hubs 时,生成的内容连接URL是 /MyWeb/signalr。当你在IISExpress中使用VD时,它会创建两个网站 - 一个在 / 中,另一个在 /MyWeb 中,所以 signalr 看起来可以工作,但是我猜你最终会得到两个不同的 hubs。请确保生成的 /signalr/hub 文件包含VD在连接URL中。 - THX-1138
我也遇到了同样的问题。发布后,它就正常工作了。 - Derek Long
2个回答

26

几天前我遇到了同样的问题。我花了两天时间找到解决方案并解决了它。经过一番严肃的调查,问题的根本原因是我自定义的signalr依赖项解析器。

最后我找到了这个链接,里面说:

替换DependencyResolver

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

注意:不要在PreApplicationStart中覆盖全局解析器,它将无法工作,或者仅在某些情况下起作用。请在PostApplicationStart(使用WebActivator)或Global.asax中进行操作。

这里要注意的是注意事项。当然,在signalr2.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);
        }
    }
}

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

对我来说,现在一切都很顺利,依赖注入也可以正常工作。但是不好的地方是,无论我到哪里搜索,David Fowler 都会说“这是设计如此”。我开始思考这个设计是否真的必要还是一个错误。

希望能帮助其他研究同样问题的人。


好的,我的回答都是基于你正在使用依赖注入的假设之上的。在回答之前,我查看了你的示例链接,并且在你的第二个链接中,OP谈论到了依赖项解析。如果你没有使用依赖项解析,请纠正我。还有一件事情需要注意,你在上面的评论中给我的链接中也使用了依赖项解析。另外,StockTickerHub是单例实例。因此它总是会给你同样的东西。但在我们的情况下,依赖项解析器会提供不同的实例。如果你愿意,我可以用更多的代码来更新我的回答。 - Yusuf Uzun
@Robert,请查看此链接,其中有一个示例解决方案,不需要NuGet依赖DLL。 - Yusuf Uzun
我现在正在查看那个链接。我也已经重新表述了我的问题,以便澄清。 - Robert
嗯...你的示例解决方案看起来和我的一样。我们处理方式基本相同。控制器是相同的。客户端js也是相同的。我只是没有从客户端得到消息。关于hubs,肯定有些基本的东西我没有理解。我的印象是只有一个实例。所以如果我请求一个hub上下文,它将是我的应用程序正在运行的单个hub实例。是否可能存在多个实例?获取代理是否会创建我的hub的新实例,使我的客户端JavaScript与其他先前实例交互? - Robert
据我所知,HubContext 在任何地方都必须包含相同的数据。当我写答案时,我假设您正在使用依赖注入。所有以前的评论都是在这个背景下进行的。我建议您将示例解决方案与您的解决方案进行比较。另一种方法是创建一个草稿解决方案并写下相同的内容。我相信这样做可以更好地理解问题所在。 - Yusuf Uzun
显示剩余6条评论

2
我曾遇到同样的问题,与IoC(例如ninject或castle)有关。 如果您将全局依赖关系解析器设置为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));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接