SignalR calling client method from outside hub usi

2019-01-13 03:40发布

问题:

I'm trying to call a client method from within a .net Web API controller action.

Can I do this?

The only post I can find that comes close to what I am looking to do is this one:

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

In there a message is sent from within an asp.net MVC controller action using GlobalHost.ConnectionManager.GetHubContext.

When I try that inside my Web API action no errors are thrown, but the method "methodInJavascript" is never invoked on the client side.

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

When I set a break point inside that action, I see that the code is being reached and executed. Nothing happens on the javascript client side though.

Why? Is Web API so different under the hood that this won't work? Has anyone else tried it and had success?

When I call the "methodInJavascript" from "within" my hub, it works perfectly. Just won't work when called from within a .net Web API controller action.

UPDATE:

After researching this issue I have no solution. I can only assume there is something missing from examples like this Server to client messages not going through with SignalR in ASP.NET MVC 4 and this calling SignalR hub from WebAPI controller issues like maybe there is an additional configuration step to enable calling from a HubContext or something. The code I initially posted here is like that which appears in those examples has not been demonstrated to be flawed in any way. Can anyone see a flaw in the code? Calling from html works. I do it extensively in my apps and never experience an issue. I have never seen a call from the HubContext in an API controller work. No errors. Just no results on the client.

SOLVED (kind of):

Code above does indeed work as is when published. Does not work in Visual Studio dev environment via localhost though. No errors but no result on the client end. Publishing the code as is to a real server on the web does indeed work. I never thought there'd be a difference so I never tried. Figured if it didn't work locally it wouldn't work published. It's working live now but I'm wondering why it doesn't work via localhost in the dev environment. Can't test locally with breakpoints and such.

I have a feeling it's that signalr virtual directory. Something is different when run locally vs published. Not sure what but I see lots of posts like http://www.bitwisejourneys.com/signalr-hosting-in-iis-a-nasty-gotcha/. Reading now to see if there's a way to have it work both locally and published.

回答1:

I came across with same issue couple days ago. That took my 2 days to find solution and resolve it. After some serious investigate the problems root cause was the signalr dependency resolver that I set customly.

At the end I found this link and that was saying this:

Replacing the DependencyResolver

You can change the DependencyResolver to use your DI container of choice by setting GlobalHost.DependencyResolver.

NOTE: DO NOT override the global resolver in PreApplicationStart, it will not work, or it'll work only sometimes. Do it in PostApplicationStart (using WebActivator) or in Global.asax.

The important place here the NOTE. Of course after signalr 2.0 this documentation become deprecated. So I mixed some of here with the new SignalR API. In new SignalR API not using WebActivatorEx anymore. OwinStartup preferred instead of 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);
        }
    }
}

And in your 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);
        }
    }
}

I dont want to send all the code here, for showing up the real problem.

So for me everything works fine for now. Dependency injection also works too. But the bad part is everywhere that I searched David Fowler was saying "Its by design". I started to think is this design really a necessary or a mistake.

Hope it helps somebody else who makes research for same problem.



回答2:

I had the same issue, and it is related to IoC (with whatever such as ninject or castle). If you set the global dependency resolver to your IoC manager, it will also replace the SignalR inner pipeline resolution. This makes your SingleTon client hub, not work correctly.

I solved it by only having the Server Hubs being IoC-ed The code below requires SignalHubActivator (you can find it on the internet)

Now, GlobalHost.ConnectionManager.GetHubContext will return the single instance AND client methods will be called correctly again!

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


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