JQuery застрял на предполетной проверке CORS и призрачном ответе IIS
Я застрял. Серьезно... - решено. читать дальше:)
Сценарий: я пытаюсь сделать правильную вещь здесь. Я добавил функциональность CORS в свою службу REST (ASP.NET Web-API), опираясь на Thinktecture Identitymodel CORS DelegatingHandler. Все идет нормально.
Чтобы проверить, работает ли он, я сделал следующее:
- Я установил простую HTML-страницу и опубликовал ее на другом хосте, чем остальные службы (xttp://otherhost/simplewebpage). Страница использует JQuery, чтобы сделать пример запроса. Код смотри ниже.
- Затем я настроил службу отдыха так, чтобы она не использовала iis express, а использовала полностью запущенный экземпляр, работающий на моей машине для разработки (xttp: // developmenthost / restservice).
- И последнее, но не менее важное: на моем компьютере для разработки я открываю страницу xttp://otherhost/simplewebpage и запускаю запрос Ajax. Обратный вызов ошибки выполняется, сообщая, что в Chrome произошла "ошибка транспорта" (IE9) или "" (пустая строка). Я удостоверился, что нет никакой связанной с прокси проблемой подключения или чем-то подобным.
Поэтому я вышел и посмотрел на следы Fiddler и журналы IIS. Fiddler говорит, что нет запроса GET /rest/hello, а есть запрос OPTIONS /rest/hello - что вполне нормально и ожидаемо! Однако ответ на запрос OPTIONS довольно интригующий!
Весь заголовок ответа выглядит так:
HTTP/1.1 200 OK
Allow: OPTIONS, TRACE, GET, HEAD, POST
Server: Microsoft-IIS/7.5
Public: OPTIONS, TRACE, GET, HEAD, POST
Date: Fri, 15 Feb 2013 14:09:27 GMT
Content-Length: 0
Это, конечно, нигде даже близко к ожидаемому ответу. Самое интересное в том, что запрос даже не попал в Application_BeginRequest() в моем приложении. Так что мое заявление не может нести ответственность за этот результат. Я вижу запрос в своих журналах IIS, и IIS добавляет заголовок Powered-by-ASP.NET.. так что он определенно проходит через (правильный) сайт IIS.
Код JQuery, который запускает запрос ajax:
function Run()
{
$.ajax({
type: 'GET',
url: url,
dataType: "json",
beforeSend: function(jqXhr) {
jqXhr.setRequestHeader("Authorization", "Basic " + getBasicHttpEncodedString(userName, password));
jqXhr.setRequestHeader("Api-Key", "123");
},
success: successCallback,
error: errorCallback,
timeout: 180*1000
});
}
Результирующий запрос OPTIONS выглядит так:
OPTIONS http://services.dev13/Rest/Hello HTTP/1.1
Host: developmenthost
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: http://otherhost/simplewebpage
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.57 Safari/537.17
Access-Control-Request-Headers: accept, origin, api-key, authorization
Accept: */*
DNT: 1
Referer: http://otherhost/simplewebpage
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
... и вы уже видели ответ на это выше.
Есть идеи, кто именно отвечает на мой запрос ОПЦИИ? Или мой код JQuery имеет недостатки? Служба REST прекрасно работает, если я использую, например, Postman (приложение Google Chrome) или подделываю запросы в Fiddler (возможно, потому, что они не выполняют согласование CORS - нет запроса OPTIONS).
Обновление № 1: Ранее сегодня я где-то читал, что отключение WebDAV является обязательным, поскольку оно мешает запросам OPTIONS. Мое представление служб IIS Role Services сообщает, что публикация WebDAV не установлена.
* Обновление № 2:* Проблема решена?? Я копал глубже. В IIS зарегистрирован модуль, который отвечает за "нежелательный (?)" Ответ на запрос OPTIONS. Его имя "OPTIONSVerbHandler" (обработчик: ProtocolSupportModule). Если я отключаю этот модуль, запрос передается в мое приложение. Там создается более значимый ответ, а затем идет фактический запрос GET! УРА!
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/7.5
Access-Control-Allow-Origin: http://otherhost/simplewebpage
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: accept,origin,api-key,authorization
X-AspNet-Version: 4.0.30319
Date: Fri, 15 Feb 2013 15:09:25 GMT
Content-Length: 0
Как только вы узнаете, где проблема, конечно, вы найдете множество ресурсов, говорящих вам, чтобы убедиться, что ваш web.config выглядит так:-/
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="false">
<remove name="WebDAVModule" />
</modules>
<handlers>
<remove name="OPTIONSVerbHandler" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
Он все еще не работает в IE9 ("ошибка: нет транспорта"). В случае, если кто-то пошел по той же дороге, что и я -> это вещь IE9: /questions/5947021/ie9-jquery-ajax-s-cors-vozvraschaet-dostup-zapreschen/5947029#5947029
2 ответа
Создайте класс PreflightRequestsHandler, в котором вы разрешаете заголовки запросов (1) и включаете cors перед вашим классом (2).
1. public class PreflightRequestsHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Headers.Contains("Origin") && request.Method.Method.Equals("OPTIONS"))
{
var response = new HttpResponseMessage { StatusCode = HttpStatusCode.OK };
// Define and add values to variables: origins, headers, methods (can be global)
response.Headers.Add("Access-Control-Allow-Origin", "*");
response.Headers.Add("Access-Control-Allow-Headers", "content-type");
response.Headers.Add("Access-Control-Allow-Methods", "*");
var tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(response);
return tsc.Task;
}
return base.SendAsync(request, cancellationToken);
}
}
2. [EnableCors(origins: "*", headers: "*", methods: "*", exposedHeaders: "X-Custom-Header")]
Ваш ответ здесь: https://gist.github.com/mathieucarbou/1114981
По сути, в IE есть довольно специфические предостережения и недостатки для cors. Однажды я сам начал писать, но потом нашел решение mathieucarbou и решил, что он лучше.
Возможно, это решение будет немного тяжелее, чем вы хотели, но для поддержки кросс-браузерного решения я считаю приемлемым использовать IE с "настраиваемым решением" настолько сложно, насколько это необходимо.
Проверьте это http://musthaan.wordpress.com/2014/12/09/set-access-control-allow-origin-in-web-api/. Это может вам помочь.