32

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.

Community
  • 1
  • 1
Robert
  • 828
  • 2
  • 13
  • 28
  • 2
    I am really not understanding this at all. I see the documentation. I see code snippets in many places. They all look the same. Has anyone actually done this? Has anyone actually seen it work? – Robert Dec 16 '13 at 03:08
  • 1
    ya i did this it doesnt work for me either. I get the same behavior as OP – godseyeview Jun 10 '14 at 01:19
  • Did you ever get to the bottom of this? – jonho Oct 11 '14 at 17:05
  • @Robert, your note about VD solved it for me. I noticed that my pages that reference /signalr/hubs were GETting localhost:1234/signalr/hubs and generated content was initializing path to /signalr instead of /MyWeb/signalr. BUT when I enter localhost:1234/MyWeb/signalr/hubs - generated content connection URL is /MyWeb/signalr. When you use VDs in IISExpress it creates two web sites - one in / and one in /MyWeb, so the signalr appears to work, but, I guess, you end up with two distinct hubs. Make sure that generated /signalr/hub file includes VD in the connection URL. – THX-1138 Nov 11 '14 at 19:21
  • I am facing this same issue. After publishing, it is working well. – Derek Long Jul 26 '21 at 04:49

2 Answers2

26

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.

Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
Yusuf Uzun
  • 1,491
  • 1
  • 13
  • 32
  • I appreciate your answer. I don't understand it however. Why would I want to replace the DependencyResolver. The documentation on calling client methods from outside the hub (http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#callfromoutsidehub) says nothing about that. None of the examples I've cited above mention it either. My code above looks correct to me. Do you see something incorrect with it? – Robert Dec 16 '13 at 03:01
  • Ok, my answer was all about assuming that you were using dependency injection. Before answer here, I checked your example links and in your second link OP was talking about dependency resolution. So please correct me if you are not using dependency resolver. And also there is one more thing here. The link which you gave me in above comment use also dependency resolver. An also that StockTickerHub is singleton instance. So it always gives you same thing back. But in our case dependency resolver gives different instances. If you want I can update my answer with some more code. – Yusuf Uzun Dec 16 '13 at 19:00
  • @Robert please check [this link](https://github.com/yusufuzun/so-signalr-question-20561196), there is an example solution without nuget dependency dlls – Yusuf Uzun Dec 16 '13 at 20:31
  • I am looking at that link now. I have also reworded my question above in an attempt to clarify. – Robert Dec 16 '13 at 21:15
  • Hmmm... Your sample solution looks like mine. We're handling essentially the same way. Controller is same. Client side js is same. I'm just not getting messages out the client side. There has to be something fundamental that I do not understand about hubs. My impression was that there was only one instance. So if I ask for a hub context, it will be to the one single hub instance that my app is running. Is it possible there are more than one instance. Does getting a proxy create a new instance of my hub leaving my client side javascript talking to some other, previous instance? – Robert Dec 16 '13 at 23:04
  • AFAIK hubcontext must always contain same data in everywhere. When I was writing answer I was assuming you using dependency injection. All Previous comments were in that context. I suggest you to compare the example solution with yours. Other way you can create a draft solution and write same things. This way I believe you can understand better what is wrong. – Yusuf Uzun Dec 17 '13 at 17:36
  • After researching this issue I have no solution. I can only assume there is something missing from examples like this http://stackoverflow.com/questions/13648605/server-to-client-messages-not-going-through-with-signalr-in-asp-net-mvc-4 and this http://stackoverflow.com/questions/16695975/calling-signalr-hub-from-webapi-controller-issues (maybe missing extra configuration steps or something?). The code I initially posted here is like that 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. From API controller does not – Robert Jul 09 '14 at 16:33
  • This problem was kicking my tail something fierce! Moving the DependencyResolver to the `Application_Start` event solved my problem too. Thank you!!! – Swisher Sweet Feb 21 '15 at 16:53
  • I've spent hours debugging this issue, this wacky solution has solved my woes. Thanks. – keithl8041 Nov 01 '16 at 22:58
  • Thanks for your post! This definitely should be marked as an answer! Yes, if you are using SignalR and have your own DI container (ex. Unity) AND you are using `GlobalHost` you should set your `GlobalHost.DependencyResolver = new MyResolver...` in `Application_Start` and NOT in `HubConfiguration` in `app.MapSignalR` Otherwise, context returned by `GlobalHost.ConnectionManager.GetHubContext` would not delver any messages or may throw 'Using a Hub instance not created by the HubPipeline is unsupported.' when you try do Client.All or any other send function. – Serge Ageyev May 21 '19 at 10:47
  • in which namespace these classes are defined? SignalrDefaultDependencyResolver & UnityConfig.Container – Waleed.alhasan Jul 24 '22 at 20:59
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));
Egbert Nierop
  • 2,066
  • 1
  • 14
  • 16