У меня есть C # RestAPI, к которому я только что добавил некоторые функциональные возможности веб-сокетов с помощью библиотеки SignalR.
Это отдельно от веб-интерфейса, также C #.
СетьВнешний интерфейс использует Javascript для создания соединения SignalR с RestAPI, пример кода приведен ниже.
В тестировании, когда веб-интерфейс и RestAPI оба находятся в LocalHost, это работает нормально (даже если пути разные,WFE находится на «localhost / wfe», а RestAPI на «localhost / restapi»). Соединение SignalR работает и делает все, что должно.
Тем не менее, когда я публикую их на нашем тестовом сервере, пути совершенно другие (WFE находится на нашем сайте our.test.server.com/Test,и RestAPI находится на «localhost: 89 / Test» на том же сервере).
Все остальное работает нормально (этот WFE и RestAPI используются уже около 10 лет, всегда на отдельных серверах), носоединение SignalR больше не работает.
Глядя на консоль веб-браузера, я вижу следующее: -
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);
});
}
}
Это остановило его работу полностью, даже на localhost
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(builder =>
{
builder.WithOrigins("https://example.com")
.AllowAnyHeader()
.WithMethods("GET", "POST")
.AllowCredentials();
});
}
}
Это даже не скомпилируется, ошибка "Невозможно преобразовать лямбда-выражение типа" 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) Разрешить CORS в RestAPI web.config
<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, я изменил тестовую систему на HTTP для WFE и RestAPI на всякий случайt, что у нас нет надлежащих сертификатов для HTTPS, все еще не работает.
Я использовал точно такой же пакет и код SignalR в других проектах, ни один из которых не требовал перекрестного происхождения, и никогда не имелпроблема с этим на тех, и как я сказал, это работает довольно счастливо в этом, если все на том же URL. К сожалению, в этом случае они должны быть отдельными.
Все остальное работает, если они раздельные (и работают годами), это просто SignalR, которого нет.
Редактировать: Ответы на вопросы
sideshowbarker: Какой код статуса HTTP ответа? Вы можете использовать панель Network в браузере devtools для проверки. Является ли это ошибкой 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 на том же сервере» - так же и API Rest на том же сервере, что иWFE или это на вашем локальном компьютере? Если он находится на том же сервере, на котором вы указали здесь, то URL-адрес концентратора SignalR, безусловно, будет нашим.test.server.com:89/Test
Извините, я не очень ясно понял.
Оба находятся на одном и том же сервере (нашем тестовом сервере), поэтому оба могут действительно получить к ним доступ с помощью http://our.test.server.com...
Однако мне необходимо проверить, когда веб-интерфейс и RestAPI работают. на разных серверах (которыми они являются, когда Live на системах наших клиентов).
Итак, в файле конфигурации я указал URL-адрес RestAPI как 'http://localhost:89/Test', а не' http://our.test.server.com:89/Test'
А-а-а, напечатав этот ответ, только что заставил меня понять, в чем проблема, СПАСИБО! :)
До сих пор WFE когда-либо общался только с RestAPI напрямую, и поэтому 'http://localhost:89/Test' относится к локальному хосту нашего тестового сервера, на котором включен WFE, и поэтомуработает.
Но, конечно, как идиот, я передаю то же значение в браузер для использования в соединении SignalR, поэтому он смотрит на мой localhost.
Изменено значение в файле конфигурации на 'http://our.test.server.othername.com:89/Test', другой URL, указывающий на одно и то же место, и теперь все это работает!
Большое спасибо, strickt01 ! :)
Редактировать # 2: Еще один вопрос
fran: но у вас все еще будет проблема в вашей производственной среде, потому что у вас будет 2 разных источника. Вы используете какую-то аутентификацию в своей сети? Я подозреваю, что в настоящее время у вас нет проблем с вызовами, потому что вы wfe звоните на контроллеры, работающие под тем же источником, тогда ваши wfe контроллеры делают вызовы к веб-сервису. В этом случае у вас нет проблемы с 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.
Просто чтобы быть уверенным, я удалил строку 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.
Итак, определенно тестирую с перекрестным происхождением и определенно работаю!
Спасибо, что заставили меняперепроверьте, хотя, всегда стоит делать! :)