【发布时间】: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