我有一个提供视图的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(显然不再是交叉来源),但随后也会获得/signalr/connect?...
和/signalr/start?...
。 JS控制台还会打印成功消息。
总结如此;
/negotiate
GET返回200,看似有效的连接ID和预期的Access-Control-Allow-Origin:
标头。这告诉我服务器端CORS支持正常运行,但信号器连接不成功。 WTF我错过了还是做错了?!可能在HttpConfiguration.EnableCors()
和IAppBuilder.UseCors(CorsOption)
之间存在一些冲突?
答案 0 :(得分:6)
解决了它。我更改了map.UseCors(CorsOptions.AllowAll)
以传递CorsPolicy
对象,并将SupportsCredentials
设置为false,并在其他位置读取Access-Control-Allow-Origin: *
与access-control-allow-credentials: true
不兼容。< / p>
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
重写。
现在它有效。
答案 1 :(得分:0)
对我来说,以下设置做得很好
services.AddCors(c =>
{
c.AddPolicy("AllowCCORSOrigin", options => options
.WithOrigins("http://localhost:3000")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
);
});