【问题标题】:C# SignalR on WebAPI rejecting CORSWebAPI 上的 C# SignalR 拒绝 CORS
【发布时间】:2020-02-13 04:02:31
【问题描述】:

我有一个 C# RestAPI,我刚刚使用 SignalR 库向其中添加了一些 websockets 功能。

这与 Web 前端以及 C# 是分开的。

Web 前端使用 Javascript 与 RestAPI 建立 SignalR 连接,示例代码如下。

在测试中,当 Web 前端和 RestAPI 都在 LocalHost 上时,这可以正常工作(即使路径不同,WFE 在“localhost/wfe”上,RestAPI 在“localhost/restapi”上)。 SignalR 连接正常工作,并完成了它应该做的一切。

但是,当我将它们发布到我们的测试服务器时,路径完全不同(WFE 位于“our.test.server.com/Test”上,RestAPI 位于同一服务器上的“localhost:89/Test”上)。

其他一切正常(此 WFE 和 RestAPI 已经使用了大约 10 年,始终在不同的服务器上),但 SignalR 连接不再有效。

查看 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.

如果所有内容都在同一台服务器上,则可以正常工作的原始代码是:-

RestAPI:

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

public class WebPortalConnection : PersistentConnection
{
    // Methods here
}

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

在尝试使 CORS 正常工作时,我尝试了以下所有建议(来自此处、Microsoft 和其他网站),但均未奏效:-

1) 安装了 nuGet 包“Microsoft.AspNet.WebApi.Cors”

添加了所有配置位,并在 WebPortalConnection 类上添加了一个装饰器以允许 CORS

没有,所以又卸载了那个。

2) 安装了 nuGet 包“Microsoft.Owin.Cors”

使用建议的各种不同组合更新了 Startup 类:-

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

没有

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

没有

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

这完全停止了它的工作,即使在本地主机上也是如此

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

这甚至无法编译,错误“无法转换 lambda 表达式类型 'CorsOptions',因为它不是委托类型”。

厨房水槽:

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

没有

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>

没有。

总是得到相同的响应(对于那些没有破坏一切的人):-

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.

我使用的 SignalR 包是:Microsoft.AspNet.SignalR.Core v2.4.1

我使用的 CORS 包是:Microsoft.Owin.Cors v4.0.1

一切最初都使用 HTTPS,我已将 WFE 和 RestAPI 的测试系统更改为 HTTP,以防万一我们没有适当的 HTTPS 证书,但仍然无法正常工作。

我在其他项目中使用了完全相同的 SignalR 包和代码,它们都不需要跨域,并且在这些项目上从来没有遇到过问题,正如我所说,如果一切正常,它在这个项目中工作得非常愉快同一个网址。不幸的是,在这种情况下,它们必须分开。

如果它们是分开的(并且多年来一直如此),其他一切都有效,只是 SignalR 不是。

编辑:问题的答案

sideshowbarker:响应的 HTTP 状态码是什么?您可以使用浏览器开发工具中的网络窗格进行检查。是 4xx 还是 5xx 错误而不是 200 OK 成功响应?

当一切都在本地主机上并且它工作时,我得到以下关于 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

当 WFE 和 RestAPI 位于不同的位置并且我遇到 CORS 故障时,我只会收到关于 SignalR 请求的以下网络响应:-

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

什么都没有,我的意思是没有响应代码,大概是因为 SignalR 没有真正响应。

strickt01 :您声明“WFE 在 our.test.server.com/Test 上,RestAPI 在 localhost:89/Test 在同一台服务器上”——Rest API 也在同一台服务器上WFE 还是在您的本地计算机上?如果它与您在此处所述位于同一台服务器上,那么 SignalR 集线器的 URL 肯定是 our.test.server.com:89/Test

对不起,我不是很清楚。

两者都在同一个服务器上(我们的测试服务器),所以两者都可以通过http://our.test.server.com...访问。

但是,我特别需要测试 Web 前端和 RestAPI 何时位于不同的服务器上(当它们在我们客户的系统上运行时)。

因此,在配置文件中,我将 RestAPI 的 URL 指定为“http://localhost:89/Test”而不是“http://our.test.server.com:89/Test

Aaaand 输入这个回复让我意识到问题所在,谢谢! :)

到目前为止,WFE 只直接与 RestAPI 对话,因此“http://localhost:89/Test”指的是 WFE 所在的测试服务器的本地主机,因此可以正常工作。

当然,我像个白痴一样,现在将相同的值传递给浏览器以供 SignalR 连接使用,所以它正在查看 my localhost。

将配置文件中的值更改为'http://our.test.server.othername.com:89/Test',不同的URL指向同一个地方,现在一切正常!

非常感谢,strict01! :)

编辑#2:另一个问题

fran:但你的生产环境中仍然会遇到问题,因为你将有 2 个不同的来源。您是否在 wfe 中使用某种身份验证?我怀疑您当前拨打电话没有任何问题,因为您 wfe 正在调用在同一来源下运行的控制器,那么您的 wfe 控制器就是正在调用 Web 服务的控制器。在这种情况下,您没有 CORS 问题,因为它是服务器到服务器的通信

我现在使用两个不同的来源。

WFE 在http://our.test.server.com/Test

RestAPI 在http://our.test.server.othername.com:89/Test

不同的 URL 和不同的端口,两者都应该引发 CORS 错误。

在测试服务器内部,WFE 将 RestAPI 从http://our.test.server.com/Test 调用到http://our.test.server.othername.com:89/Test。他们在同一台服务器上,但他们实际上并不知道。

从用户的角度来看,我在浏览器中登录到http://our.test.server.com/Test。就浏览器而言,这就是起源。

当我进行 SignalR 调用时,它会连接到 http://our.test.server.othername.com:89/Test,因为这是配置文件中的 URL,它作为要连接的 URL 传递。

SignalR 确实将此视为跨域连接,我在控制台输出中得到以下信息:-

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.

为了确定,我从 Startup 类中删除了行 app.UseCors(CorsOptions.AllowAll); 并再次尝试。

这次它因 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.

所以,绝对是跨源测试,绝对有效!

谢谢你让我仔细检查,总是值得做的! :)

【问题讨论】:

  • 响应的 HTTP 状态码是什么?您可以使用浏览器开发工具中的网络窗格进行检查。是 4xx 还是 5xx 错误而不是 200 OK 成功响应?
  • 您声明“WFE 位于 our.test.server.com/Test,而 RestAPI 位于同一服务器上的 localhost:89/Test” - Rest API 与 WFE 位于同一服务器上,还是位于您的本地计算机?如果它与您在此处所述位于同一台服务器上,那么 SignalR 集线器的 URL 肯定是 our.test.server.com:89/Test?
  • 但是您的生产环境中仍然会遇到问题,因为您将有 2 个不同的来源。您是否在 wfe 中使用某种身份验证?我怀疑您当前拨打电话没有任何问题,因为您 wfe 正在调用在同一来源下运行的控制器,那么您的 wfe 控制器就是正在调用 Web 服务的控制器。在这种情况下,您没有 CORS 问题,因为它是服务器到服务器的通信

标签: c# asp.net-web-api cors signalr


【解决方案1】:

已解决,感谢 strict01!

正如我在回答问题的编辑中所说,我是个白痴,并使用“本地主机”来指代两个完全不同的地方。

指定一个实际的站点名称(与主站点不同,但指向同一个地方)解决了这个问题。

为了完整起见,工作代码是...

RestAPI:

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):

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

【讨论】:

    猜你喜欢
    • 2014-08-06
    • 2016-04-25
    • 2017-04-28
    • 1970-01-01
    • 1970-01-01
    • 2020-11-16
    • 2014-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多