Как пропустить предварительный запрос 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);
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.