Почему отправляется запрос OPTIONS и можно ли его отключить?

Я строю веб-API. Я обнаружил, что всякий раз, когда я использую Chrome для POST, GET для моего API, всегда есть запрос OPTION, отправляемый перед реальным запросом, что довольно раздражает. В настоящее время я получаю сервер, чтобы игнорировать любой запрос ОПЦИИ. Теперь мои вопросы: что хорошего в отправке запроса OPTION для удвоения нагрузки на сервер? Есть ли способ полностью запретить браузеру отправлять запросы OPTIONS?

17 ответов

Решение

edit 2018-09-13: добавлены некоторые уточнения относительно этого запроса перед полетом и как его избежать в конце этого ответа.

OPTIONS запросы это то, что мы называем pre-flight запросы в Cross-origin resource sharing (CORS),

Они необходимы, когда вы делаете запросы из разных источников в определенных ситуациях.

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

Ваш сервер не должен игнорировать, а обрабатывать эти запросы всякий раз, когда вы пытаетесь выполнить запросы из разных источников.

Хороший ресурс можно найти здесь http://enable-cors.org/

Чтобы справиться с этим, чтобы чувствовать себя комфортно, это обеспечить, чтобы на любом пути с OPTIONS метод сервер отправляет ответ с этим заголовком

Access-Control-Allow-Origin: *

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

Для получения дополнительной информации о том, как добавить поддержку CORS на ваш сервер, смотрите следующую блок-схему

http://www.html5rocks.com/static/images/cors_server_flowchart.png

Блок-схема CORS


редактировать 2018-09-13

CORS OPTIONS запрос запускается только в некоторых случаях, как описано в документах MDN:

Some requests don’t trigger a CORS preflight. Those are called “simple requests” in this article, though the Fetch spec (which defines CORS) doesn’t use that term. A request that doesn’t trigger a CORS preflight—a so-called “simple request”—is one that meets all the following conditions:

The only allowed methods are:
- GET
- HEAD
- POST

Apart from the headers set automatically by the user agent (for example, Connection, User-Agent, or any of the other headers with names defined in the Fetch spec as a “forbidden header name”), the only headers which are allowed to be manually set are those which the Fetch spec defines as being a “CORS-safelisted request-header”, which are:
- Accept
- Accept-Language
- Content-Language
- Content-Type (but note the additional requirements below)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width

The only allowed values for the Content-Type header are:
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain

No event listeners are registered on any XMLHttpRequestUpload object used in the request; these are accessed using the XMLHttpRequest.upload property.

No ReadableStream object is used in the request.

Прошел эту проблему, ниже мой вывод на эту проблему и мое решение.

В соответствии со стратегией CORS (настоятельно рекомендуем вам прочитать об этом) Вы не можете просто заставить браузер прекратить отправку запроса OPTION, если он сочтет это необходимым.

Есть два способа обойти это

    1. Убедитесь, что ваш запрос является "простым запросом"
    1. Задавать Access-Control-Max-Age для запроса ОПЦИИ

Простой запрос

Простой межсайтовый запрос - это тот, который удовлетворяет всем следующим условиям:

Единственные допустимые методы: - GET - HEAD - POST

Помимо заголовков, автоматически устанавливаемых пользовательским агентом (например, Connection, User-Agent и т. Д.), Единственными заголовками, которые разрешено устанавливать вручную, являются: - Accept - Accept-Language - Content-Language - Content-Type

Допустимые значения для заголовка Content-Type: - application/x-www-form-urlencoded - multipart/form-data - text/plain

Простой запрос не вызовет опциональный запрос перед полетом.

Установить кеш для проверки OPTION

Вы можете установить Access-Control-Max-Age для запроса OPTION, чтобы он не проверял разрешение снова, пока не истечет срок его действия.

Access-Control-Max-Age дает значение в секундах, в течение которого можно кэшировать ответ на запрос предварительной проверки без отправки другого запроса предварительной проверки.

Пожалуйста, обратитесь к этому ответу о фактической потребности в предварительном запросе OPTIONS: CORS - Какова мотивация введения предварительных запросов?

Чтобы отключить запрос OPTIONS, для ajax-запроса должны быть выполнены следующие условия:

  1. Запрос не устанавливает пользовательские заголовки HTTP, такие как "application/xml" или "application/json" и т. Д.
  2. Метод запроса должен быть одним из GET, HEAD или POST. Если POST, тип контента должен быть одним из application/x-www-form-urlencoded, multipart/form-data, или же text/plain

Ссылка: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Когда у вас откроется консоль отладки и Disable Cache Опция включена, предварительные запросы будут отправляться всегда (т.е. перед каждым запросом). если вы не отключите кеш, предполетный запрос будет отправлен только один раз (на сервер)

Да, можно избежать запроса параметров. Запрос параметров - это предварительный запрос при отправке (публикации) любых данных в другой домен. Это проблема безопасности браузера. Но мы можем использовать другую технологию: транспортный слой iframe. Я настоятельно рекомендую вам забыть о любой конфигурации CORS и использовать готовое решение, и оно будет работать где угодно.

Посмотрите здесь: https://github.com/jpillora/xdomain

И рабочий пример: http://jpillora.com/xdomain/

Хорошего дня!

Для разработчика, который понимает причину его существования, но нуждается в доступе к API, который не обрабатывает вызовы OPTIONS без аутентификации, мне нужен временный ответ, чтобы я мог разрабатывать локально, пока владелец API не добавит надлежащую поддержку SPA CORS или я не получу прокси-API и работает.

Я обнаружил, что вы можете отключить CORS в Safari и Chrome на Mac.

Отключить ту же политику происхождения в Chrome

Chrome: выйдите из Chrome, откройте терминал и вставьте эту команду: open /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir

Safari: отключение политики одного источника в Safari

Если вы хотите отключить политику того же происхождения в Safari (у меня есть 9.1.1), вам нужно только включить меню разработчика и выбрать "Отключить ограничения перекрестного происхождения" в меню разработки.

Как уже упоминалось в предыдущих постах, OPTIONS запросы там по причине. Если у вас есть проблема с большим временем отклика от вашего сервера (например, соединение за границей), вы также можете настроить браузер для кэширования предварительных запросов.

Ваш сервер отвечает с Access-Control-Max-Age заголовок и для запросов, которые идут к той же конечной точке, предварительный запрос будет кэширован и больше не будет выполняться.

Я решил эту проблему, как.

if($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && ENV == 'devel') {
    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: X-Requested-With');
    header("HTTP/1.1 200 OK");
    die();
}

Это только для развития. С этим я жду 9мс и 500мс, а не 8с и 500мс. Я могу сделать это, потому что производственное приложение JS будет на той же машине, что и производственное, поэтому не будет OPTIONS но развитие мое местное.

Вы не можете, но вы можете избежать CORS, используя JSONP.

вы также можете использовать диспетчер API (например, Gravitee.io с открытым исходным кодом), чтобы предотвратить проблемы CORS между внешним приложением и серверными службами, манипулируя заголовками в предварительной проверке.

Заголовок, используемый в ответ на предварительный запрос, чтобы указать, какие заголовки HTTP можно использовать при выполнении фактического запроса:

  • Тип содержимого
  • заголовок разрешения-управления-доступом
  • авторизация
  • x-просил-с

и укажите "allow-origin" = localhost:4200 например

Проведя полтора дня, пытаясь решить подобную проблему, я обнаружил, что это связано с IIS.

Мой проект Web API был настроен следующим образом:

// WebApiConfig.cs
public static void Register(HttpConfiguration config)
{
    var cors = new EnableCorsAttribute("*", "*", "*");
    config.EnableCors(cors);
    //...
}

У меня не было конкретных параметров конфигурации CORS в узле web.config > system.webServer, как я видел во многих постах

Нет конкретного кода CORS в global.asax или в контроллере в качестве декоратора

Проблема была в настройках пула приложений.

Режим управляемого конвейера был установлен на классический (изменил его на интегрированный), а Identity был установлен на Network Service (изменил его на ApplicationPoolIdentity)

Изменение этих настроек (и обновление пула приложений) исправило это для меня.

Запрос OPTIONS - это функция веб-браузеров, поэтому отключить его непросто. Но я нашел способ перенаправить его с помощью прокси . Это полезно в случае, если конечная точка службы просто еще не может обрабатывать CORS/OPTIONS, возможно, все еще находится в разработке или неправильно настроена.

Шаги:

  1. Настройте обратный прокси для таких запросов с помощью инструментов по выбору (nginx, YARP, ...)
  2. Создайте конечную точку только для обработки запроса OPTIONS. Может быть проще создать обычную пустую конечную точку и убедиться, что она хорошо обрабатывает CORS.
  3. Настройте два набора правил для прокси. Один из них - направить все запросы OPTIONS на фиктивную конечную точку, указанную выше. Другой - для маршрутизации всех остальных запросов к рассматриваемой конечной точке.
  4. Обновите веб-сайт, чтобы вместо него использовать прокси.

В основном этот подход заключается в том, чтобы обмануть браузер, чтобы запрос OPTIONS работал. Учитывая, что CORS предназначен не для повышения безопасности, а для ослабления политики одного и того же происхождения, я надеюсь, что этот трюк какое-то время сработает. :)

Одно из решений, которое я использовал в прошлом, - скажем, ваш сайт находится на mydomain.com, и вам нужно сделать ajax-запрос к foreigndomain.com

Настройте перезапись IIS с вашего домена на чужой домен - например,

<rewrite>
  <rules>
    <rule name="ForeignRewrite" stopProcessing="true">
        <match url="^api/v1/(.*)$" />
        <action type="Rewrite" url="https://foreigndomain.com/{R:1}" />
    </rule>
  </rules>
</rewrite>

на вашем сайте mydomain.com - вы можете сделать тот же запрос о происхождении, и нет необходимости в запросе опций:)

Я думаю, вы отправляете запрос на кросс-домен.

Для междоменных запросов установка типа содержимого, отличного от application / x-www-form-urlencoded, multipart / form-data или text/plain, заставит браузер отправлять предварительный запрос OPTIONS на сервер.

Поэтому вам может понадобиться указать contentType, чтобы избежать запроса OPTION.

Пример Jquery:-

$.ajax({
    url: "http://crossdomainurl",
    type: "POST",
    contentType: 'text/plain'
}); 

Возможно, есть решение (но я его не тестировал): вы можете использовать CSP (Content Security Policy), чтобы включить удаленный домен, и браузеры могут пропустить проверку запроса CORS OPTIONS.

Я, если найду время, протестирую и обновлю этот пост!

CSP: https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Security-Policy

Спецификация CSP: https://www.w3.org/TR/CSP/

Что мне помогло, так это импортировать "github.com/gorilla/handlers", а затем использовать его следующим образом:

router := mux.NewRouter()
router.HandleFunc("/config", getConfig).Methods("GET")
router.HandleFunc("/config/emcServer", createEmcServers).Methods("POST")

headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOk := handlers.AllowedOrigins([]string{"*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})

log.Fatal(http.ListenAndServe(":" + webServicePort, handlers.CORS(originsOk, headersOk, methodsOk)(router)))

Как только я выполнил запрос POST Ajax и прикрепил к нему данные JSON, Chrome всегда добавлял заголовок Content-Type, которого не было в моей предыдущей конфигурации AllowedHeaders.

Это может быть решено в случае использования прокси, который перехватывает запрос и записывает соответствующие заголовки. В частном случае Varnish это будут правила:

if (req.http.host == "CUSTOM_URL" ) {
set resp.http.Access-Control-Allow-Origin = "*";
if (req.method == "OPTIONS") {
   set resp.http.Access-Control-Max-Age = "1728000";
   set resp.http.Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, PATCH, OPTIONS";
   set resp.http.Access-Control-Allow-Headers = "Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since";
   set resp.http.Content-Length = "0";
   set resp.http.Content-Type = "text/plain charset=UTF-8";
   set resp.status = 204;
}

}

Другие вопросы по тегам