Cross-domain will not work with a SignalR PersistentConnection

halter73 picture halter73 · Mar 12, 2013 · Viewed 8.5k times · Source

NOTE: Someone else originally asked this question but deleted it before I could post my answer. Since this question covers many issues that developers face when trying to make SignalR work cross-domain, I decided to replicate it. Plus, I had already finished writing the answer!

I'm running a SignalR 1.0.1 server in an ASP.NET MVC .NET Framework 4 project. I have another ASP.NET application on a different domain (different localhost port) trying to connect via the JavaScript client. I get this when my application tries to connect:

XMLHttpRequest cannot load http://localhost:31865/api/negotiate?_=1363105027533.
Origin http://localhost:64296 is not allowed by Access-Control-Allow-Origin.

I've followed all steps to enable cross-domain support with SignalR -- what am I missing?

  • jQuery.support.cors = true;
  • $.connection('http://localhost:31865/api', '', false, { jsonp: true, xdomain: true });
  • RouteTable.Routes.MapHubs(new HubConfiguration { EnableCrossDomain = true });
  • RouteTable.Routes.MapConnection<ApiConnection>("/api", "api");

I also added the following to Web.config in the API project:

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Access-Control-Allow-Origin" value="*" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

I'm using a PersistentConnection for my SignalR server, not hubs.

Any ideas?

Answer

halter73 picture halter73 · Mar 12, 2013

MapHubs will configure an endpoint at /signalr for communicating with all your Hub classes. Since you are not using Hubs, the call to MapHubs is unnecessary. The call to MapHubs also does not have any effect on the configuration of your /api connection.

Your call to MapConnection should be changed to read as follows:

RouteTable.Routes.MapConnection<ApiConnection>("api", "api",
    new ConnectionConfiguration { EnableCrossDomain = true });

NOTE: The second argument to MapConnection is the URL. The first argument is the route name. The / is unnecessary, but doesn't really hurt in either case.

  • Setting jQuery.support.cors = true; should ONLY be done "To enable cross-domain requests in environments that do not support cors yet but do allow cross-domain XHR requests (windows gadget, etc)" [1]. This does not pertain to any versions of IE or any other browser that I know of. If the browser does not support CORS, SignalR will already automatically fall back to JSONP unless you set jQuery.support.cors to true.

    If you just set this to true blindly, SignalR will assume that the environment does support cross-domain XHR requests and not automatically fall back to JSONP rendering SignalR unable to establish cross-domain connections while running in browsers that truly don't support CORS.

  • $.connection('http://localhost:31865/api', '', false, { jsonp: true, xdomain: true }); is incorrect. You should only need

    var connection = $.connection('http://localhost:31865/api');
    

    xdomain is no longer an option for the SignalR JS client, and if you really want to specify jsonp, you should do it when you start the connection like so:

     connection.start({ jsonp: true}).done(function () { /* ... */ });
    

    I should reiterate that SignalR will automatically fall back to JSONP if the environment does not support CORS, so you should not specify this option yourself. JSONP does not require an Access-Control-Allow-Origin header, but it does force SignalR to use its most inefficient transport: long polling.

  • You do not need to setup customHeaders in your Web.config. SignalR will set the Access-Control-Allow-Origin header in SignalR responses automatically when you set EnableCrossDomain to true in you ConnectionConfiguration.

Reference https://github.com/SignalR/SignalR/wiki/QuickStart-Persistent-Connections for more advice abut using PersistentConnections.