Как пропустить предварительный запрос OPTIONS?

93

Я разработал приложение PhoneGap, которое сейчас трансформируется в мобильный сайт. Все работает плавно, кроме одного небольшого глюка. Я использую определенный сторонний API через запрос POST, который отлично работает в приложении, но не работает в мобильной версии веб-сайта.

При более внимательном рассмотрении кажется, что AngularJS (я предполагаю, что браузер) сначала отправляет запрос OPTIONS. Сегодня я многое узнал о CORS, но не могу понять, как его вообще отключить. У меня нет доступа к этому API (поэтому изменения на этой стороне невозможны), но они добавили домен, над которым я работаю, в свой заголовок Access-Control-Allow-Origin.

Это код, о котором я говорю:

        var request = {
                language: 'fr',
                barcodes: [
                    {
                        barcode: 'somebarcode',
                        description: 'Description goes here'
                    }
                ]
            };
        }
        var config = {
            headers: { 
                'Cache-Control': 'no-cache',
                'Content-Type': 'application/json'
            }
        };
        $http.post('http://somedomain.be/trackinginfo', request, config).success(function(data, status) {
            callback(undefined, data);
        }).error(function(data, status) {
            var err = new Error('Error message');
            err.status = status;
            callback(err);
        });

Как я могу запретить браузеру (или AngularJS) отправлять этот запрос OPTIONS и просто перейти к фактическому запросу POST? Я использую AngularJS 1.2.0.

Заранее спасибо.

Брэм Вандевалль
источник

Ответы:

100

Предполетная проверка запускается вашим Content-Type of application/json. Самый простой способ предотвратить это - установить Content-Type для text/plainвашего случая. application/x-www-form-urlencoded& multipart/form-dataContent-Types также приемлемы, но вам, конечно же, необходимо соответствующим образом отформатировать полезные данные запроса.

Если после внесения этого изменения вы все еще видите предполетную проверку, то Angular может также добавить X-заголовок к запросу.

Или у вас могут быть заголовки (Authorization, Cache-Control ...), которые будут запускать его, см.:

Рэй Николус
источник
9
Это правильный ответ - ваши заголовки Content-Type и Cache-Control запускают предпечатный запрос. Простой GET с Content-Type text / plain и несколько других - единственные способы инициировать запрос без предварительной проверки. См .: developer.mozilla.org/en-US/docs/HTTP/…
Джефф Хаббард,
Ах да, забыл про Cache-Control.
Ray Nicholus
17
Пользовательский заголовок также запустит предпечатную проверку.
Себастьен Депре
2
Изменение типа содержимого для предотвращения тестирования OPTIONs - это не ответ. Тип контента должен соответствовать типу контента независимо от этого
ekerner
если это бросает браузер и в бэкэнде, Http-метод OPTIONS заблокирован, будет ли это иметь какой-либо эффект, например, что браузер не будет вызывать соответствующий API для POST / PUT, поскольку OPTIONS не удалось?
P Satish Patro
16

Как сказал Рэй, вы можете остановить это, изменив заголовок содержимого, например:

 $http.defaults.headers.post["Content-Type"] = "text/plain";

Например -

angular.module('myApp').factory('User', ['$resource','$http',
    function($resource,$http){
        $http.defaults.headers.post["Content-Type"] = "text/plain";
        return $resource(API_ENGINE_URL+'user/:userId', {}, {
            query: {method:'GET', params:{userId:'users'}, isArray:true},
            getLoggedIn:{method:'GET'}
        });
    }]);

Или прямо на звонок -

var req = {
 method: 'POST',
 url: 'http://example.com',
 headers: {
   'Content-Type': 'text/plain'
 },
 data: { test: 'test' }
}

$http(req).then(function(){...}, function(){...});

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

ПРИМЕЧАНИЕ. Запрос не должен иметь никаких настраиваемых параметров заголовка. Если заголовок запроса содержит какой-либо настраиваемый заголовок, браузер сделает предполетный запрос, вы не сможете его избежать.

вивекс
источник
1
Как мне это сделать просто с помощью $http?
Learnercys
Спасибо, это похоже на то, что я делал. Единственные изменения - это метод GETи дополнительный заголовок Authorization. Но все же предполетная отправка.
Learnercys
1
Вы можете вставить сюда свой запрос? как завиток что ли? Возможно, это из-за заголовка авторизации, попробуйте удалить его, а затем попробуйте. Если вы отправляете собственные заголовки, то angular отправит предполетный запрос.
vivex
pastebin.com/vRDeFiH2 Первый - это запрос к $ http, а второй - действительный запрос,
создающий
2

При выполнении определенных типов междоменных запросов AJAX современные браузеры, поддерживающие CORS, вставляют дополнительный запрос «предварительной проверки», чтобы определить, есть ли у них разрешение на выполнение действия. Из примера запроса:

$http.get( ‘https://example.com/api/v1/users/’ +userId,
  {params:{
           apiKey:’34d1e55e4b02e56a67b0b66’
          }
  } 
);

В результате этого фрагмента мы видим, что на адрес было отправлено два запроса (OPTIONS и GET). Ответ сервера включает заголовки, подтверждающие допустимость запроса GET. Если ваш сервер не настроен для правильной обработки запроса OPTIONS, клиентские запросы не будут выполнены. Например:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: accept, origin, x-requested-with, content-type
Access-Control-Allow-Methods: DELETE
Access-Control-Allow-Methods: OPTIONS
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Methods: GET
Access-Control-Allow-Methods: POST
Access-Control-Allow-Orgin: *
Access-Control-Max-Age: 172800
Allow: PUT
Allow: OPTIONS
Allow: POST
Allow: DELETE
Allow: GET
М. Пустота
источник
2

Я думаю, что лучший способ - проверить, имеет ли запрос тип "OPTIONS" return 200 из среднего ПО. У меня это сработало.

express.use('*',(req,res,next) =>{
      if (req.method == "OPTIONS") {
        res.status(200);
        res.send();
      }else{
        next();
      }
    });
Девбрат Рагхуванши
источник
Это работает, но в OWASP рекомендуется не раскрывать ОПЦИИ. Вы блокируете его погодой в бэкэнд / хостинговом сервисе (Nginx, Apache) и т. Д.
P Satish Patro,
0

Предварительная проверка - это функция веб-безопасности, реализованная браузером. Для Chrome вы можете отключить всю веб-безопасность, добавив флаг --disable-web-security.

Например: «C: \ Program Files \ Google \ Chrome \ Application \ chrome.exe» --disable-web-security --user-data-dir = «C: \ newChromeSettingsWithoutSecurity». Сначала вы можете создать новый ярлык хрома, перейти к его свойствам и изменить цель, как указано выше. Это должно помочь!

Ариджит Патра
источник
Вы же не можете ожидать, что OP скажет своим клиентам отключить безопасность браузера только для того, чтобы включить функцию, верно ?!
Сварог
@svarog это в основном для целей разработчиков, в основном на производственном сервере вы не столкнетесь с этой проблемой.
Ариджит Патра,
если это бросает браузер и в бэкэнде, Http-метод OPTIONS заблокирован, будет ли это иметь какой-либо эффект, например, что браузер не будет вызывать соответствующий API для POST / PUT, поскольку OPTIONS не удалось?
P Satish Patro
-2

установка типа содержимого на undefined приведет к тому, что javascript будет передавать данные заголовка как есть и перезаписывать конфигурации заголовка angular $ httpProvider по умолчанию. Документация по Angular $ http

$http({url:url,method:"POST", headers:{'Content-Type':undefined}).then(success,failure);
Теофил Оморегби
источник