Запрос HTTP OPTIONS на веб-сайтах Azure завершается с ошибкой из-за CORS

Недавно я переместил наши серверы с Rackspace CloudSites (работающих на Apache/Linux) на веб-сайты Windows Azure. После миграции все запросы jQuery AJAX в нашем REST API начали давать сбой из-за CORS.

Мы используем настраиваемые заголовки, поэтому jQuery выполняет предварительный HTTP-запрос OPTIONS перед запуском реальных вызовов API. Проблема в том, что запрос OPTIONS, похоже, не достигает моего PHP-кода и вместо этого возвращается каким-то другим объектом (очевидно, веб-сервером), над которым я, похоже, не контролирую.

Я использую следующие заголовки уже пару лет, поэтому я уверен, что проблема не в PHP-коде:

<?php
    $this->output->set_header("Access-Control-Allow-Origin: *");
    $this->output->set_header("Access-Control-Allow-Methods: GET,POST,DELETE,HEAD,PUT,OPTIONS");
    $this->output->set_header("Access-Control-Allow-Headers: X-Olaround-Debug-Mode, Authorization, Accept");
    $this->output->set_header("Access-Control-Expose-Headers: X-Olaround-Debug-Mode, X-Olaround-Request-Start-Timestamp, X-Olaround-Request-End-Timestamp, X-Olaround-Request-Time, X-Olaround-Request-Method, X-Olaround-Request-Result, X-Olaround-Request-Endpoint" );
?>

Я предполагаю, что проблема связана с веб-сайтами Azure, поскольку код, похоже, отлично работает и на моем компьютере для разработки (Windows 8/IIS 8.0). Я новичок в Azure (и в хостинге на базе Windows в целом), поэтому я почти не знаю, как подойти к этой проблеме и отладить ее, поскольку веб-сайты Azure предоставляют очень минимальный контроль.


person Uzair Sajid    schedule 24.09.2013    source источник
comment
Возвращается ли запрос OPTIONS с кодом состояния 405?   -  person Ray Nicholus    schedule 24.09.2013
comment
@RayNicholus На самом деле я получаю HTTP 404 Not Found со следующим ответным сообщением на вкладке «Сети» консоли разработчика Chrome: в запрошенном ресурсе отсутствует заголовок «Access-Control-Allow-Origin». Таким образом, доступ к источнику 'olman-dev.azurewebsites.net' запрещен.   -  person Uzair Sajid    schedule 25.09.2013


Ответы (3)


Я решил опубликовать полное решение этой проблемы, поскольку уже предоставленные ответы (хотя и технически правильные) не работают в этом конкретном случае для меня. Хитрость заключалась в следующем:

1. Добавьте <customHeaders> в <httpProtocol> в web.config

Как @hcoat также предложил выше, добавление system.webServer.httpProtocol.customHeaders было первым шагом для решения проблемы (я уже пробовал это раньше, но это не сработало). Добавьте сюда все настраиваемые заголовки и методы HTTP, которые необходимо установить для CORS.

<httpProtocol>
    <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Methods" value="GET,POST,DELETE,HEAD,PUT,OPTIONS" />
        <add name="Access-Control-Allow-Headers" value="Origin, X-Olaround-Debug-Mode, Authorization, Accept" />
        <add name="Access-Control-Expose-Headers" value="X-Olaround-Debug-Mode, X-Olaround-Request-Start-Timestamp, X-Olaround-Request-End-Timestamp, X-Olaround-Request-Time, X-Olaround-Request-Method, X-Olaround-Request-Result, X-Olaround-Request-Endpoint" />
    </customHeaders>
</httpProtocol>

2. Переопределите обработчик по умолчанию для PHP и удалите OPTIONSVerbHandler.

Следующий шаг (решение, предоставленное @Bing Han) — удалить OPTIONSVerbHandler по умолчанию, определенный в IIS, а также установить собственный обработчик PHP54_via_FastCGI, который принимает ваши дополнительные методы HTTP. Обработчик по умолчанию работает только с запросами GET, POST и HEAD.

<handlers>
    <remove name="OPTIONSVerbHandler" />
    <remove name="PHP54_via_FastCGI" />
    <add name="PHP54_via_FastCGI" path="*.php" verb="GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK" modules="FastCgiModule" scriptProcessor="D:\Program Files (x86)\PHP\v5.4\php-cgi.exe" resourceType="Either" requireAccess="Script" />
</handlers>

Взгляните на эту публикацию для получения более подробной информации о внутренние работы.

3. Удалите все заголовки ответов, установленные в коде вашего приложения.

Это была последняя часть головоломки, которая вызывала больше всего проблем. Поскольку IIS уже добавлял <customHeaders>, фрагмент кода PHP, которым я поделился в вопросе выше, дублировал их. Это вызывало проблемы на уровне браузера, который плохо реагировал на несколько заголовков одного типа.

Последний web.config, который помог решить эту проблему

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="Imported Rule 1" stopProcessing="true">
                    <match url="^(.*)$" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{R:1}" pattern="^(dir_path\.php|lolaround|lolaround\.php|app_assets)" ignoreCase="false" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="lolaround.php/{R:1}" />
                </rule>
                <rule name="Imported Rule 2" stopProcessing="true">
                    <match url="lolaround/(.*)" ignoreCase="false" />
                    <action type="Rewrite" url="/lolaround.php/{R:1}" />
                </rule>
            </rules>
        </rewrite>
        <httpProtocol>
            <customHeaders>
                <add name="Access-Control-Allow-Origin" value="*" />
                <add name="Access-Control-Allow-Methods" value="GET,POST,DELETE,HEAD,PUT,OPTIONS" />
                <add name="Access-Control-Allow-Headers" value="Origin, X-Olaround-Debug-Mode, Authorization, Accept" />
                <add name="Access-Control-Expose-Headers" value="X-Olaround-Debug-Mode, X-Olaround-Request-Start-Timestamp, X-Olaround-Request-End-Timestamp, X-Olaround-Request-Time, X-Olaround-Request-Method, X-Olaround-Request-Result, X-Olaround-Request-Endpoint" />
            </customHeaders>
        </httpProtocol>
        <handlers>
            <remove name="OPTIONSVerbHandler" />
            <remove name="PHP54_via_FastCGI" />
            <add name="PHP54_via_FastCGI" path="*.php" verb="GET, PUT, POST, HEAD, OPTIONS, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK" modules="FastCgiModule" scriptProcessor="D:\Program Files (x86)\PHP\v5.4\php-cgi.exe" resourceType="Either" requireAccess="Script" />
        </handlers>
    </system.webServer>
</configuration>

Примечание. Хотя ответы @hcoat и @Bing Han были полезны в этой задаче, я могу присудить награду только одному из них. Я решил передать его @Bing Han, потому что его ответ приблизил меня к решению (и я не смог найти способ добавить собственный обработчик PHP из собственного поиска).

Обновление: я отредактировал ответ, чтобы добавить поддержку метода HTTP DELETE, который отсутствовал в исходном ответе.

person Uzair Sajid    schedule 30.09.2013
comment
Я случайно внес изменения, не авторизовавшись. Ну что ж :) - person Uzair Sajid; 14.10.2013
comment
Спасибо за подробный ответ, но, к сожалению, у меня это не сработало (я использую IIS 7.5). После 2 дней исследований единственным рабочим решением, которое я нашел, чтобы избежать 405 Method Not Allowed, было определение заголовков CORS в методе Application_BeginRequest, как указано в этом ответе stackoverflow.com/a/14631068/827168 - person pomeh; 15.10.2014
comment
@pomeh На самом деле, название вопроса не очень понятно, но это решение на самом деле специфично для запуска PHP на веб-сайтах Azure. В любом случае это не сработало бы с ASP.NET :) - person Uzair Sajid; 15.10.2014
comment
вы правы, это решение связано с PHP, но пункты 1 и 3 не зависят от языка. Кроме того, этот вопрос является первым результатом поиска Google azure cors, поэтому я хотел поделиться найденным решением, которое сработало для меня, чтобы помочь другим людям! :) Чтобы быть исчерпывающим, вот список возможных альтернативных решений: stackoverflow.com/a/13229373/827168 stackoverflow.com/a/15599025/827168 stackoverflow.com/a/20705500/827168 stackoverflow.com/a/15619435/827168. Но IMO лучший ответ - использовать отслеживание неудачных запросов stackoverflow.com/a/6223774/827168 - person pomeh; 15.10.2014
comment
Вы забыли DELETE в последнем примере web.config. Дополнительное примечание для тех, кто пришел сюда намного позже: 54 и 5.4 следует заменить на 56 и 5.6 или любую другую версию PHP, которую вы используете с веб-приложением IIS PHP. - person JustAMartin; 18.02.2016

Запрос HTTP OPTIONS завершается неудачно, так как обработчик PHP-CGI по умолчанию не обрабатывает команду «OPTIONS».

Добавьте следующий код в файл web.config, чтобы решить проблему.

<configuration>
  <system.webServer>
    <!-- 
      Some other settings 
    -->
    <handlers>
      <remove name="OPTIONSVerbHandler" />
      <remove name="PHP54_via_FastCGI" />
      <add name="PHP54_via_FastCGI" path="*.php" verb="GET,HEAD,POST,OPTIONS" modules="FastCgiModule" scriptProcessor="D:\Program Files (x86)\PHP\v5.4\php-cgi.exe" resourceType="Either" />
    </handlers>
  </system.webServer>
</configuration>

У меня есть запись в блоге по этому поводу: http://tekblg.blogspot.sg/2013/09/azure-websites-php-cross-domain-request.html

person Bing Han    schedule 30.09.2013
comment
Я перешел по ссылке, которой вы поделились, и провел дополнительные исследования, и технически ваше решение должно работать само по себе. Но в моем случае и этого было недостаточно. Оказывается, если я добавлю customHeaders в web.config (как ответил @hcoat) И УДАЛЮ свой собственный из PHP-кода, то API начинает вести себя нормально. Я добавлю еще один ответ ниже с точным кодом, необходимым для его работы. - person Uzair Sajid; 30.09.2013
comment
Привет @UzairSajid. Я только что проверил с пользовательским заголовком, например. header('Access-Control-Expose-Headers: X-Olaround-Debug-Mode, X-Olaround-Request-Start-Timestamp, X-Olaround-Request-End-Timestamp, X-Olaround-Request-Time, X-Olaround-Request-Method, X-Olaround-Request-Result, X-Olaround-Request-Endpoint');, и смог получить это в заголовке ответа. Вы не можете установить это через PHP header и можете установить только через web.config? Пока для всех моих приложений я могу установить любой пользовательский заголовок через PHP. Просто любопытно :) - person Bing Han; 30.09.2013
comment
по-видимому, только установка заголовков в PHP не работала. IIS по-прежнему отвечал на запросы вместо того, чтобы передавать их обработчику PHP54_via_FastCGI, если только я не закодировал <customHeaders> в web.config. Как только обработчик PHP получает управление, заголовки PHP начинают работать. Вот почему я получаю дубликаты, поэтому я просто обусловил код заголовка из PHP, если он работал в Azure (через переменную среды). - person Uzair Sajid; 02.10.2013

На сервере Windows вы не можете полагаться на заголовки php для CORS. Вам нужно создать web.config в корне сайта или корне приложения, содержащее что-то вроде следующего.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <system.webServer>
   <httpProtocol>
     <customHeaders>
       <add name="Access-Control-Allow-Origin" value="*" />
       <add name="Access-Control-Allow-Methods" value="GET,POST,DELETE,HEAD,PUT,OPTIONS" />
       <add name="Access-Control-Allow-Headers" value="X-Olaround-Debug-Mode, Authorization, Accept" />
       <add name="Access-Control-Expose-Headers" value="X-Olaround-Debug-Mode, X-Olaround-Request-Start-Timestamp, X-Olaround-Request-End-Timestamp, X-Olaround-Request-Time, X-Olaround-Request-Method, X-Olaround-Request-Result, X-Olaround-Request-Endpoint" />
     </customHeaders>
   </httpProtocol>
 </system.webServer>
</configuration>

Этот процесс сравним с настройкой файла .htaccess, суть в том, что вы можете просто создать его там, где вам нужно, без перенастройки вашего сервера.

person hcoat    schedule 29.09.2013
comment
Я уже пробовал это. Проблема по-прежнему сохраняется, и IIS выдает ошибку 404, несмотря на возврат правильных заголовков ответа. - person Uzair Sajid; 30.09.2013