交叉原点SignalR连接在协商后停止 [英] Cross origin SignalR connection stops after negotiate

查看:623
本文介绍了交叉原点SignalR连接在协商后停止的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个MVC 5应用程序提供视图,和一个Web API 2应用程序作为服务层(.NET 4.5)。 Web API应用程序使用SignalR 2.1.2在处理POST到服务API时返回进度。两者部署到不同的域,因此我已按照 asp.net 教程文章。

  [assembly:OwinStartup(typeof(Startup))] 
命名空间MyApp.Service
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map(/ signalr,map =>
{
//担心将其锁定到特定的来源)
map.UseCors(CorsOptions。 AllowAll);
map.RunSignalR(new HubConfiguration());
});
//现在启动WebAPI应用程序
GlobalConfiguration.Configure(WebApiConfig.Register);
}
}
}

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

  namespace MyApp.Service 
{
public static class WebApiConfig
{
public static void注册(HttpConfiguration config)
{
//控制器调用将来自MVC项目,部署到
//不同域,因此必须启用跨源资源共享
config.EnableCors();
// Web API路由
config.MapHttpAttributeRoutes();

// Snip其他控制器依赖初始化
}
}
}

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

  namespace MyApp.Service.Hubs 
{
[HubName(testresult)]
public class TestResultHub:Hub
{
}
}

由于我要跨域并且集线器没有暴露任何服务器端API,我不打扰使用生成的JS代理。



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

  function TestScenarioHandler(signalrHubUrl){
var self = this;
// Snip无关位(主要是Knockout初始化)

self.signalrConnectionId = ko.observable();

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

var hubProxy = hubConnection.createHubProxy(testresult);
hubProxy.on(progress,function(value){
console.log(Hooray!从服务器获得一个新值:+ 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);
});
}

确认Chrome显示相同的东西)GET到



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



$ c> name 匹配集线器类上 HubName 属性的值。



这个GET返回HTTP 200,响应给我一个JSON有效载荷,包含 ConnectionId ConnectionToken 其他字段,建议一切正常。 HTTP响应还具有设置到GET发起的域的 Access-Control-Allow-Origin:头。



但是JS控制台打印无法连接到signalr hub在http:/ / / **** service.azurewebsites.net/signalr



为了验证我没有做任何愚蠢的事,我添加了signalr支持和一个基本的中心到MVC应用程序(所以没有跨源需要),并改变了 $。hubConnection() hubConnection.createProxy )相应地调用。当我这样做,浏览器流量显示相同的 / signalr / negotiate?... GET(显然不是交叉源),但然后也GETs到 / signalr / connect?... / signalr / start?...






  • < CORS在服务层上启用,并且signalr / negotiate GET返回200,看起来是有效的连接ID,以及预期的 Access- Control-Allow-Origin:头。这表明服务器端CORS支持本身正确,但signalr连接不成功。

  • 当我重新配置,所以signalr连接不是交叉原点,一切工作作为预期。



WTF我失踪或做错了吗? HttpConfiguration.EnableCors() IAppBuilder.UseCors(CorsOption)之间可能有一些冲突?

解决方案

解决了。我改变了 map.UseCors(CorsOptions.AllowAll)以传递 CorsPolicy 对象,并设置<$在其他地方读过 Access-Control-Allow-Origin:* 不兼容时,c $ c> SupportsCredentials 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());
});
//现在启动WebAPI应用程序
GlobalConfiguration.Configure(WebApiConfig.Register);
}

设置 SupportCredentials true会导致以实际原点(不是 * )重写 Access-Control-Allow-Origin code> access-control-allow-credentials:true 。



b $ b

I have an MVC 5 app serving up views, and a Web API 2 app as the service layer (.NET 4.5). The Web API app uses SignalR 2.1.2 to return progress as it's processing POSTs to the service API. The two are deployed to different domains, so I've set up cross origin support as per the asp.net tutorial article.

[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 also contains its own CORS declaration.

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
        }
    }
}

I've defined a simple hub class with no server-side API (it's only to allow the server to push to the clients, not for the clients to call into).

namespace MyApp.Service.Hubs
{
    [HubName("testresult")]
    public class TestResultHub : Hub
    {
    }
}

Since I'm going cross-domain AND the hub is not exposing any server side API, I'm not bothering to use a generated JS proxy.

The relevant bits of the JS that set up the signalr hub connection is: (remember this is being served up from the MVC app, which does not have any signalr support (except jquery-signalr-{version}.js of course))

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);
        });
}

Going cross-origin like this, Firefox network traffic shows (and I've confirmed Chrome shows the same thing) a GET to

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

Notice that the name matches the value of the HubName attribute on my hub class.

This GET returns HTTP 200, the response gives me a JSON payload containing a ConnectionId, ConnectionToken, and a bunch of other fields that suggests everything's ok. The HTTP response also has the Access-Control-Allow-Origin: header set to the domain that the GET originated from. All up it looks good, except that's where the traffic stops.

But the JS console prints "Failed to connect to signalr hub at http://****service.azurewebsites.net/signalr"

To verify I'm not doing anything too stupid, I've added signalr support and a basic hub to the MVC app (so no cross origin required), and changed the $.hubConnection() and hubConnection.createProxy() calls accordingly. When I do that, browser traffic shows the same /signalr/negotiate?... GET (obviously not cross origin any more), but then also GETs to /signalr/connect?... and /signalr/start?.... The JS console also prints a success message.

So in summary;

  • CORS is enabled on the service layer, and the signalr /negotiate GET returns 200, what appears to be a valid connection id, and the expected Access-Control-Allow-Origin: header. This suggests to me that the server-side CORS support is behaving itself correctly, but the signalr connection does not succeed.
  • When I reconfigure so the signalr connection is NOT cross origin, everything works as expected.

WTF am I missing or doing wrong?! Some conflict between HttpConfiguration.EnableCors() and IAppBuilder.UseCors(CorsOption) perhaps?

解决方案

Solved it. I had changed the map.UseCors(CorsOptions.AllowAll) to pass in a CorsPolicy object instead, and set SupportsCredentials to false, having read elsewhere that Access-Control-Allow-Origin: * is incompatible with 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);
}

Setting SupportCredentials to true results in the Access-Control-Allow-Origin header being rewritten with the actual origin (not *) and access-control-allow-credentials: true in the response.

And now it works.

这篇关于交叉原点SignalR连接在协商后停止的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆