【发布时间】: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,信号器
/negotiateGET 返回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"。我不知道它为什么会这样做,也不知道它为什么会有所作为。
标签: asp.net-web-api signalr cors signalr-hub