C # SignalR в WebAPI, отклоняющем CORS - PullRequest
       57

C # SignalR в WebAPI, отклоняющем CORS

0 голосов
/ 16 октября 2019

У меня есть 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.

Итак, определенно тестирую с перекрестным происхождением и определенно работаю!

Спасибо, что заставили меняперепроверьте, хотя, всегда стоит делать! :)

1 Ответ

0 голосов
/ 16 октября 2019

Решено, спасибо strickt01!

Как я уже говорил в редактировании, где я отвечал на вопросы, я был идиотом и использовал localhost для обозначения двух совершенно разных мест.

Указание фактического имени сайта (отличного от основного сайта, но указывающего на то же место) решило проблему.

Только для полноты, рабочий код ...

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