简体   繁体   English

WebAPI 上的 C# SignalR 拒绝 CORS

[英]C# SignalR on WebAPI rejecting CORS

I have a C# RestAPI, to which I've just added some websockets functionality using the SignalR library.我有一个 C# RestAPI,我刚刚使用 SignalR 库向其中添加了一些 websockets 功能。

This is separate to the Web Front End, also C#.这与 Web 前端以及 C# 是分开的。

The Web Front End uses Javascript to make a SignalR connection to the RestAPI, sample code below. Web 前端使用 Javascript 与 RestAPI 建立 SignalR 连接,示例代码如下。

In testing, when the Web Front End and the RestAPI are both on LocalHost, this works fine (even though the paths are different, WFE is on 'localhost/wfe' and RestAPI is on 'localhost/restapi').在测试中,当 Web 前端和 RestAPI 都在 LocalHost 上时,这可以正常工作(即使路径不同,WFE 在“localhost/wfe”上,RestAPI 在“localhost/restapi”上)。 The SignalR connection works, and does everything it's supposed to. SignalR 连接工作正常,并完成了它应该做的一切。

However, when I publish them to our test server, the paths are completely different (WFE is on 'our.test.server.com/Test', and RestAPI is on 'localhost:89/Test' on the same server).但是,当我将它们发布到我们的测试服务器时,路径完全不同(WFE 位于“our.test.server.com/Test”上,而 RestAPI 位于同一服务器上的“localhost:89/Test”上)。

Everything else works fine (this WFE and RestAPI have been in use live for about 10 years now, always on separate servers), but the SignalR connection no longer works.其他一切正常(此 WFE 和 RestAPI 已经使用了大约 10 年,始终在单独的服务器上),但 SignalR 连接不再有效。

Looking at the Web Browser console, I see the following:-查看 Web 浏览器控制台,我看到以下内容:-

SignalR: Auto detected cross domain url.
SignalR: Negotiating with 'http://localhost:89/Test/socket/negotiate?clientProtocol=2.1&type=requestfile&userid=df4e6ce5-a666-42e5-8c0c-57c9f6f76a0e&documentid=b87c430a-03d8-47ba-bd5e-5913f895d0a6'.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:89/Test/socket/negotiate?clientProtocol=2.1…umentid=b87c430a-03d8-47ba-bd5e-5913f895d0a6&_=1571222295312. (Reason: CORS request did not succeed).
SignalR: Stopping connection.

The original code, which works fine if everything on the same server, is:-如果所有内容都在同一台服务器上,原始代码可以正常工作,它是:-

RestAPI:休息API:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.MapSignalR<WebPortalConnection>("/socket");
    }
}

public class WebPortalConnection : PersistentConnection
{
    // Methods here
}

WFE (Javascript): WFE(Javascript):

var socketPath = 'Value passed from config file, e.g. http://localhost:89/Test/';

var requestFile = $.connection(socketPath + "socket", "type=requestfile&userid=" + currentUserID + "&documentid=" + documentID, true);

requestFile.received(function (data) {
    // Do Stuff Here
});

In attempting to get CORS working, I have tried all the following suggestions (from here, Microsoft, and other sites), none of which worked:-在试图让 CORS 工作时,我尝试了以下所有建议(来自这里、微软和其他网站),但都没有奏效:-

1) Installed the nuGet package "Microsoft.AspNet.WebApi.Cors" 1) 安装了 nuGet package "Microsoft.AspNet.WebApi.Cors"

Added all the configuration bits, and a decorator on the WebPortalConnection class to allow CORS在 WebPortalConnection class 上添加了所有配置位和装饰器以允许 CORS

Nope, so uninstalled that one again.没有,所以又卸载了那个。

2) Installed the nuGet package "Microsoft.Owin.Cors" 2) 安装了 nuGet package "Microsoft.Owin.Cors"

Updated the Startup class with various different combinations of things suggested:-使用建议的各种不同组合更新了 Startup class:-

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.MapSignalR<WebPortalConnection>("/socket");
        app.UseCors(CorsOptions.AllowAll);
    }
}

Nope没有

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCors(CorsOptions.AllowAll);
        app.MapSignalR<WebPortalConnection>("/socket");
    }
}

Nope没有

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Map("/socket", map =>
        {
            map.UseCors(CorsOptions.AllowAll);
            var hubConfiguration = new HubConfiguration { };
            map.RunSignalR(hubConfiguration);
        });
    }
}

This stopped it working entirely, even on localhost即使在本地主机上,这也完全停止了它的工作

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCors(builder =>
        {
            builder.WithOrigins("https://example.com")
                .AllowAnyHeader()
                .WithMethods("GET", "POST")
                .AllowCredentials();
        });
    }
}

This wouldn't even compile, error "Cannot convert lambda expression type 'CorsOptions' because it is not a delegate type'.这甚至无法编译,错误“无法转换 lambda 表达式类型 'CorsOptions',因为它不是委托类型”。

Kitchen sink:厨房水槽:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.MapSignalR<WebPortalConnection>("/socket");
        app.UseCors(CorsOptions.AllowAll);
        app.Map("/socket", map =>
        {
            map.UseCors(CorsOptions.AllowAll);
            var hubConfiguration = new HubConfiguration { };
            map.RunSignalR(hubConfiguration);
        });
    }
}

Nope没有

3) Allow CORS in the RestAPI web.config 3) 在 RestAPI web.config 中允许 CORS

<httpProtocol>
  <customHeaders>
    <clear />
    <add name="Access-Control-Allow-Origin" value="*" />
    <add name="Access-Control-Allow-Methods" value="*" />
    <add name="Access-Control-Allow-Credentials" value="true" />
  </customHeaders>
</httpProtocol>

Nope.没有。

Always get the same response (on those that didn't break everything):-总是得到相同的回应(那些没有破坏一切的人): -

SignalR: Auto detected cross domain url.
SignalR: Negotiating with 'http://localhost:89/Test/socket/negotiate?clientProtocol=2.1&type=requestfile&userid=df4e6ce5-a666-42e5-8c0c-57c9f6f76a0e&documentid=b87c430a-03d8-47ba-bd5e-5913f895d0a6'.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:89/Test/socket/negotiate?clientProtocol=2.1…umentid=b87c430a-03d8-47ba-bd5e-5913f895d0a6&_=1571222295312. (Reason: CORS request did not succeed).
SignalR: Stopping connection.

The SignalR package I'm using is: Microsoft.AspNet.SignalR.Core v2.4.1我正在使用的 SignalR package 是:Microsoft.AspNet.SignalR.Core v2.4.1

The CORS package I'm using is: Microsoft.Owin.Cors v4.0.1我正在使用的 CORS package 是:Microsoft.Owin.Cors v4.0.1

Everything was originally using HTTPS, I've changed the Test system over to HTTP for both the WFE and RestAPI just in case it was the fact that we don't have proper certificates for HTTPS, still doesn't work.一切最初都使用 HTTPS,我已将 WFE 和 RestAPI 的测试系统更改为 HTTP,以防万一我们没有 Z0E8433F9A404F21F3BA601C1 的适当证书,但仍然无法正常工作。0

I've used the exact same SignalR package and code on other projects, none of which required cross-origin, and never had a problem with it on those, and as I said it works quite happily in this one if everything's on the same URL.我已经使用了完全相同的Z358E3E185C2A6A39F30F926E4EE5BZ ZEFE90A8E604A7C840E840E88D03A67F67F67F67D8Z和代码和其他项目上的其他项目,这些项目都不需要交叉origin,并且在这些问题上都没有任何问题,并且在这些问题上,我对此一无所知。 . Unfortunately, in this case they have to be separate.不幸的是,在这种情况下,它们必须分开。

Everything else works if they're separate (and has for years), it's just SignalR that's not.如果它们是分开的(并且已经存在多年),其他一切都有效,只是 SignalR 不是。

Edit: Answers to questions编辑:问题的答案

sideshowbarker: What's the HTTP status code of the response? sideshowbarker:响应的 HTTP 状态码是什么? You can use the Network pane in browser devtools to check.您可以使用浏览器开发工具中的网络窗格进行检查。 Is it 4xx or 5xx error rather than a 200 OK success response?是 4xx 还是 5xx 错误而不是 200 OK 成功响应?

When everything is on localhost and it works, I get the following network responses in regards to the SignalR request:-当一切都在本地主机上并且它工作时,我得到以下关于 SignalR 请求的网络响应:-

200 GET localhost   negotiate?clientProtocol=2.1&<my passed parameters>
101 GET localhost   connect?transport=webSockets&clientProtocol=2.1&<my passed parameters>&connectionToken=<token>&tid=4
200 GET localhost   start?transport=webSockets&clientProtocol=2.1&<my passed parameters>&connectionToken=<token>&_=1571231671489
200 GET localhost   negotiate?clientProtocol=2.1&<my passed parameters>&_=1571231671490
101 GET localhost   connect?transport=webSockets&clientProtocol=2.1&<my passed parameters>&connectionToken=<token>&tid=6
200 GET localhost   start?transport=webSockets&clientProtocol=2.1&<my passed parameters>&connectionToken=<token>&_=1571231671491

When the WFE and RestAPI are in separate places and I get the CORS failure, I only get the following network response in regards to the SignalR request:-当 WFE 和 RestAPI 位于不同的位置并且我收到 CORS 故障时,我只收到关于 SignalR 请求的以下网络响应:-

<nothing> GET localhost:89 negotiate?clientProtocol=2.1&<my passed parameters>&_=1571231504762

By nothing, I mean there is no response code, presumably because SignalR didn't actually respond.没什么,我的意思是没有响应代码,大概是因为 SignalR 实际上没有响应。

strickt01: You state that "WFE is on our.test.server.com/Test, and RestAPI is on localhost:89/Test on the same server" - so is the Rest API on the same server as the WFE or is it on your local computer? strickt01: You state that "WFE is on our.test.server.com/Test, and RestAPI is on localhost:89/Test on the same server" - so is the Rest API on the same server as the WFE or is it on你的本地电脑? If it's on the same server as you state here then the URL for the SignalR hub would surely be our.test.server.com:89/Test If it's on the same server as you state here then the URL for the SignalR hub would surely be our.test.server.com:89/Test

Sorry, I wasn't very clear there.抱歉,我不是很清楚那里。

Both are on the same server (our test server), so both can indeed by accessed by http://our.test.server.com ...两者都在同一台服务器(我们的测试服务器)上,因此两者都可以通过http://our.test.server.com访问...

However, I specifically need to test when the Web Front End and the RestAPI are on different servers (which they are when Live on our clients' systems).但是,我特别需要测试 Web 前端和 RestAPI 何时位于不同的服务器上(当它们在我们客户的系统上运行时)。

So, in the config file, I specified the URL for the RestAPI as ' http://localhost:89/Test ' rather than ' http://our.test.server.com:89/Test ' So, in the config file, I specified the URL for the RestAPI as ' http://localhost:89/Test ' rather than ' http://our.test.server.com:89/Test '

Aaaand typing up this reply has just made me realise what the problem is, THANK YOU: :) Aaaand 输入这个回复让我意识到问题是什么,谢谢::)

Up until now, the WFE has only ever spoken to the RestAPI directly, and so ' http://localhost:89/Test ' refers to the local host for our test server that the WFE is on, and so that works.到目前为止,WFE 只直接与 RestAPI 对话,因此“ http://localhost:89/Test ”指的是 WFE 所在的测试服务器的本地主机,因此可以正常工作。

But of course like an idiot I'm passing the same value to the browser now for the SignalR connection to use, so it's looking at my localhost.但是当然像一个白痴一样,我现在将相同的值传递给浏览器以使用 SignalR 连接,所以它正在查看我的本地主机。

Changed the value in the config file to ' http://our.test.server.othername.com:89/Test ', different URL pointing to the same place, and now it all works!将配置文件中的值更改为' http://our.test.server.othername.com:89/Test ',不同的 URL 指向相同的地方,现在都可以使用!

Big thanks, strickt01 : :)非常感谢, strict01 : :)

Edit #2: Another question编辑#2:另一个问题

fran: but you are still going to have a problem in your production environment because you will have 2 different origins. fran:但是您的生产环境中仍然会遇到问题,因为您将有 2 个不同的来源。 are you using some sort of authentication in your wfe?您是否在 wfe 中使用某种身份验证? I suspect you don't have any problem making calls currently because you wfe is calling into controllers running under the same origin, then your wfe controllers are the ones that are making the calls to the web service.我怀疑您当前拨打电话没有任何问题,因为您 wfe 正在调用在同一来源下运行的控制器,那么您的 wfe 控制器就是正在调用 web 服务的控制器。 In this case you don't have a CORS issue because it's server to server communication在这种情况下,您没有 CORS 问题,因为它是服务器到服务器的通信

I'm using two different origins now.我现在使用两个不同的来源。

The WFE is on http://our.test.server.com/Test WFE 位于http://our.test.server.com/Test

The RestAPI is on http://our.test.server.othername.com:89/Test RestAPI 位于http://our.test.server.othername.com:89/Test

Different URLs and different ports, both of which should throw a CORS error.不同的 URL 和不同的端口,两者都应该抛出 CORS 错误。

Internally within the test server, the WFE is calling the RestAPI from http://our.test.server.com/Test to http://our.test.server.othername.com:89/Test .在测试服务器内部,WFE 正在从http://our.test.server.com/Test 调用 RestAPIhttp://our.test.server.othername.Z4D236D9A2D102C508AD1C50DA4BEC They are on the same server, but they don't actually know that.他们在同一台服务器上,但他们实际上并不知道。

From the User perspective, in my browser I log on to http://our.test.server.com/Test .从用户的角度来看,在我的浏览器中,我登录到http://our.test.server.com/Test That's the origin as far as the browser is concerned.就浏览器而言,这就是起源。

When I make a SignalR call, that connects to http://our.test.server.othername.com:89/Test because that's the URL in the config file, which is passed as the URL to connect to. When I make a SignalR call, that connects to http://our.test.server.othername.com:89/Test because that's the URL in the config file, which is passed as the URL to connect to.

SignalR does treat this as a cross domain connection, I get the following in the Console output:- SignalR 确实将此视为跨域连接,我在控制台 output 中得到以下信息:-

SignalR: Auto detected cross domain url.
SignalR: Negotiating with 'http://our.test.server.othername.com:89/Test/socket/negotiate?clientProtocol=2.1&t<my passed parameters>'.
SignalR: webSockets transport starting.
SignalR: Connecting to websocket endpoint 'ws://our.test.server.othername.com:89/Test/socket/connect?transport=webSockets&clientProtocol=2.1&t<my passed parameters>&connectionToken=<token>&tid=0'.
SignalR: Websocket opened.
SignalR: webSockets transport connected. Initiating start request.
SignalR: The start request succeeded. Transitioning to the connected state.
SignalR: Now monitoring keep alive with a warning timeout of 13333.333333333332, keep alive timeout of 20000 and disconnecting timeout of 30000.

Just to be sure, I removed the line app.UseCors(CorsOptions.AllowAll);可以肯定的是,我删除了app.UseCors(CorsOptions.AllowAll);from the Startup class and tried again.从启动 class 并再次尝试。

This time it failed with a CORS error:-这次它失败并出现 CORS 错误:-

SignalR: Auto detected cross domain url.
SignalR: Negotiating with 'http://our.test.server.othername.com:89/Test/socket/negotiate?clientProtocol=2.1&<my passed parameters>'.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://our.test.server.othername.com:89/Test/socket/negotiate?clientProtocol=2.1&<my passed parameters>&_=1571242017811. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
SignalR: Stopping connection.

So, definitely testing with cross origin, and definitely working!因此,绝对是跨源测试,并且绝对有效!

Thanks for making me double-check though, always worth doing: :)谢谢你让我仔细检查,总是值得做的::)

Solved, thanks to strickt01!解决了,感谢strict01!

As I stated in the edit where I answered the questions, it was me being an idiot and using 'localhost' to refer to two entirely separate places.正如我在回答问题的编辑中所说的那样,我是个白痴并使用“本地主机”来指代两个完全不同的地方。

Specifying an actual site name (different to the main site, but pointing to the same place) fixed the issue.指定一个实际的站点名称(与主站点不同,但指向同一个地方)解决了这个问题。

Just for completeness, the working code is...只是为了完整起见,工作代码是......

RestAPI:休息API:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseCors(CorsOptions.AllowAll);
        app.MapSignalR<WebPortalConnection>("/socket");
    }
}

public class WebPortalConnection : PersistentConnection
{
    // Methods here
}

WFE (Javascript): WFE(Javascript):

var socketPath = 'Value passed from config file, e.g. http://our.test.server.othername.com:89/Test/';

var requestFile = $.connection(socketPath + "socket", "type=requestfile&userid=" + currentUserID + "&documentid=" + documentID, true);

requestFile.received(function (data) {
    // Do Stuff Here
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM