交叉原点SignalR连接在协商后停止

ton*_*nes 6 cors signalr asp.net-web-api signalr-hub

我有一个MVC 5应用程序提供视图,一个Web API 2应用程序作为服务层(.NET 4.5).Web API应用程序使用SignalR 2.1.2在处理POST到服务API时返回进度.这两个部署到不同的域,所以我根据asp.net教程文章设置了跨源支持.

[assembly: OwinStartup(typeof (Startup))]
namespace MyApp.Service
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.Map("/signalr", map =>
            {
                //worry about locking it down to specific origin later
                map.UseCors(CorsOptions.AllowAll);
                map.RunSignalR(new HubConfiguration());
            });
            //now start the WebAPI app
            GlobalConfiguration.Configure(WebApiConfig.Register);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

WebApiConfig.cs还包含自己的CORS声明.

namespace MyApp.Service
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            //controller invocations will come from the MVC project which is deployed to a
            //different domain, so must enable cross origin resource sharing
            config.EnableCors();
            // Web API routes
            config.MapHttpAttributeRoutes();

            //Snip other controller dependency initialisation
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经定义了一个没有服务器端API的简单集线器类(它只允许服务器推送到客户端,而不是客户端调用).

namespace MyApp.Service.Hubs
{
    [HubName("testresult")]
    public class TestResultHub : Hub
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

由于我要跨域并且集线器没有公开任何服务器端API,所以我不打算使用生成的JS代理.

设置信号器集线器连接的JS的相关位是:(记住这是从MVC应用程序提供的,它没有任何信号器支持(当然除了jquery-signalr- {version} .js))

function TestScenarioHandler(signalrHubUrl) {
    var self = this;
//Snip irrelevant bits (mostly Knockout initialisation)

    self.signalrConnectionId = ko.observable();

    var hubConnection = $.hubConnection(signalrHubUrl, { useDefaultPath: false });

    var hubProxy = hubConnection.createHubProxy("testresult");
    hubProxy.on("progress", function(value) {
        console.log("Hooray! Got a new value from the server: " + value);
    });

    hubConnection.start()
        .done(function() {
            self.signalrConnectionId(hubConnection.id);
            console.log("Connected to signalr hub with connection id " + hubConnection.id);
        })
        .fail(function() {
            console.log("Failed to connect to signalr hub at " + hubConnection.url);
        });
}
Run Code Online (Sandbox Code Playgroud)

像这样跨越起源,Firefox网络流量显示(我已经确认Chrome显示同样的事情)GET to

http://****service.azurewebsites.net/signalr/negotiate?clientProtocol=1.5&connectionData=[{"name":"testresult"}]&_=1424419288550

请注意,name匹配HubName我的集线器类的属性值.

这get返回HTTP 200,响应给了我包含一个JSON有效载荷ConnectionId,ConnectionToken以及很多其他的领域.这表明一切正常.HTTP响应还将Access-Control-Allow-Origin:标头设置为GET源自的域.一切都看起来不错,除了交通停止的地方.

但JS控制台打印 "Failed to connect to signalr hub at http://****service.azurewebsites.net/signalr"

为了验证我没有做任何太愚蠢的事情,我已经为MVC应用添加了信号器支持和基本集线器(因此不需要交叉源),并相应地更改$.hubConnection()hubConnection.createProxy()调用.当我这样做时,浏览器流量显示相同的/signalr/negotiate?...GET(显然不再是交叉原点),但是然后GETs /signalr/connect?.../signalr/start?....JS控制台还会打印成功消息.

总而言之;

  • 在服务层上启用CORS,并且信号器/negotiateGET返回200,看起来是有效的连接ID,以及预期的Access-Control-Allow-Origin:头.这向我建议服务器端CORS支持正常运行,但信号器连接不成功.
  • 当我重新配置所以信号器连接不是交叉原点时,一切都按预期工作.

WTF我错过了还是做错了?!HttpConfiguration.EnableCors()和之间有些冲突IAppBuilder.UseCors(CorsOption)吗?

ton*_*nes 7

解决了它.我改变了map.UseCors(CorsOptions.AllowAll)传入一个CorsPolicy对象,并设置SupportsCredentials为false,读取与其他地方Access-Control-Allow-Origin: *不相容的内容access-control-allow-credentials: true.

private static readonly Lazy<CorsOptions> SignalrCorsOptions = new Lazy<CorsOptions>(() =>
{
    return new CorsOptions
    {
        PolicyProvider = new CorsPolicyProvider
        {
            PolicyResolver = context =>
            {
                var policy = new CorsPolicy();
                policy.AllowAnyOrigin = true;
                policy.AllowAnyMethod = true;
                policy.AllowAnyHeader = true;
                policy.SupportsCredentials = false;
                return Task.FromResult(policy);
            }
        }
    };
});

public void Configuration(IAppBuilder app)
{
    app.Map("/signalr", map =>
    {
        map.UseCors(SignalrCorsOptions.Value);
        map.RunSignalR(new HubConfiguration());
     });
     //now start the WebAPI app
     GlobalConfiguration.Configure(WebApiConfig.Register);
}
Run Code Online (Sandbox Code Playgroud)

设置SupportCredentials为true会导致Access-Control-Allow-Origin标头被实际原点(非*)和access-control-allow-credentials: true响应重写.

现在它有效.