协商后跨源 SignalR 连接停止
Posted
技术标签:
【中文标题】协商后跨源 SignalR 连接停止【英文标题】:Cross origin SignalR connection stops after negotiate 【发布时间】:2015-04-21 21:08:20 【问题描述】:我有一个 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);
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
我定义了一个没有服务器端 API 的简单集线器类(它只是允许服务器推送给客户端,而不是让客户端调用)。
namespace MyApp.Service.Hubs
[HubName("testresult")]
public class TestResultHub : Hub
由于我要跨域,并且集线器没有公开任何服务器端 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);
);
像这样跨域,Firefox 网络流量显示(我已经确认 Chrome 显示相同的东西)一个 GET 到
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(显然不再跨源),但随后也 GET 到 /signalr/connect?...
和 /signalr/start?...
。 JS 控制台也会打印成功消息。
总之;
在服务层启用了CORS,信号器/negotiate
GET 返回200,这似乎是一个有效的连接ID,以及预期的Access-Control-Allow-Origin:
标头。这向我表明服务器端 CORS 支持的行为正确,但信号器连接不成功。
当我重新配置信号器连接不跨源时,一切正常。
我错过了还是做错了?! HttpConfiguration.EnableCors()
和 IAppBuilder.UseCors(CorsOption)
之间可能有些冲突?
【问题讨论】:
Curioser 和 curioser... 似乎当信号器 JS 客户端确定它是一个跨域请求时,它会将 http Content-Type 从"application/json; charset=UTF-8"
更改为 "application/x-www-form-urlencoded; charset=UTF-8"
。我不知道它为什么会这样做,也不知道它为什么会有所作为。
【参考方案1】:
解决了。我已将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);
将SupportCredentials
设置为true 会导致Access-Control-Allow-Origin
标头被重写为响应中的实际来源(不是*
)和access-control-allow-credentials: true
。
现在它可以工作了。
【讨论】:
我有同样的问题,但这个解决方案没有解决它,还有其他线索吗? 在我的情况下,我必须像上面那样创建自定义策略,而是使用SupportsCredentials = true
。
我的问题通过将默认 map.UseCors(CorsOptions.AllowAll) 替换为上面提供的解决方案得到了解决。大反响。现在 cors orgin 标头出现在所有 signalR 响应标头中。【参考方案2】:
对我来说,以下设置做得很好
services.AddCors(c =>
c.AddPolicy("AllowCCORSOrigin", options => options
.WithOrigins("http://localhost:3000")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
);
);
【讨论】:
添加 .AllowCredentials() 对我有用。谢谢以上是关于协商后跨源 SignalR 连接停止的主要内容,如果未能解决你的问题,请参考以下文章