jQuery $ .ajax (), $ .post отправляет «OPTIONS» как REQUEST_METHOD в Firefox


330

Возникли проблемы с тем, что я считал относительно простым плагином jQuery ...

Плагин должен извлекать данные из php-скрипта через ajax, чтобы добавить опции в <select>. Ajax-запрос довольно общий:

$.ajax({
  url: o.url,
  type: 'post',
  contentType: "application/x-www-form-urlencoded",
  data: '{"method":"getStates", "program":"EXPLORE"}',
  success: function (data, status) {
    console.log("Success!!");
    console.log(data);
    console.log(status);
  },
  error: function (xhr, desc, err) {
    console.log(xhr);
    console.log("Desc: " + desc + "\nErr:" + err);
  }
});

Кажется, это работает нормально в Safari. В Firefox 3.5 REQUEST_TYPEна сервере всегда есть «OPTIONS», а данные $ _POST не отображаются. Apache регистрирует запрос как тип «OPTIONS»:

::1 - - [08/Jul/2009:11:43:27 -0500] "OPTIONS sitecodes.php HTTP/1.1" 200 46

Почему этот вызов ajax работает в Safari, но не в Firefox, и как мне это исправить для Firefox?

Заголовки ответа
Дата: ср, 08 июл 2009 21:22:17 по Гринвичу
Сервер: Apache / 2.0.59 (Unix) PHP / 5.2.6 DAV / 2
X-Powered-By: PHP / 5.2.6
Контент-длина 46
Keep-Alive timeout = 15, max = 100
Keep-Alive соединения
Content-Type text / html

Заголовки запроса
Форма заказа хоста: 8888
Пользовательский агент Mozilla / 5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv: 1.9.1) Gecko / 20090624 Firefox / 3.5
Принять текст / html, application / xhtml + xml, application / xml; q = 0,9, * / *; q = 0,8
Accept-Language en-us, en; q = 0,5
Принять кодировку gzip, deflate
Accept-Charset ISO-8859-1, utf-8; q = 0,7, *; q = 0,7
Keep-Alive 300
Поддержание связи
Происхождение http://ux.inetu.act.org
Access-Control-Request-Метод POST
Заголовки-Access-Control-Request-x

Вот изображение вывода Firebug:


Можете ли вы опубликовать ответ Firebug и запросить заголовки. Я не получаю никаких ошибок при запуске аналогичного кода в Firefox.
MitMaro

Добавлена ​​информация заголовка и картинка из Firebug.
fitzgeraldsteele

Просто была такая же проблема при реализации встроенного веб-сервера. Спасибо за вопрос :)
Роберт Гулд

Если вы ищете решения Java JAX-RS, см. Здесь: Access-Control-Allow-Origin
Тобиас Сарнов

Поведение firefox, кажется, изменилось сейчас? Я не получаю никаких вариантов запросов.
Buge

Ответы:


169

Причиной ошибки является та же политика происхождения. Это только позволяет вам делать запросы XMLHTTP к вашему собственному домену. Посмотрите, можете ли вы использовать обратный вызов JSONP :

$.getJSON( 'http://<url>/api.php?callback=?', function ( data ) { alert ( data ); } );

26
почему Firefox единственный браузер, который делает это? Я хочу пост не получить.
Маслоу

11
Crossite-POST: кто-нибудь знает решение сделать POST с application / json как Content-Type?
schoetbi

13
Так в чем же заключается решение?
Ник Так

3
Поиск решения этого вопроса и использование getJSON вместо вызова ajax не делает этого для меня, так как он намного более ограничен.
Тимо Валлениус

1
@schoetbi для этого вам понадобится использовать CORS, который хорошо поддерживается в новых браузерах ... ограниченная поддержка в IE8-9 и требует поддержки на стороне сервера.
Tracker1

57

Я использовал следующий код на стороне Django, чтобы интерпретировать запрос OPTIONS и установить требуемые заголовки Access-Control. После этого мои междоменные запросы от Firefox начали работать. Как уже было сказано, браузер сначала отправляет запрос OPTIONS, а затем сразу после этого отправляет запрос POST / GET.

def send_data(request):
    if request.method == "OPTIONS": 
        response = HttpResponse()
        response['Access-Control-Allow-Origin'] = '*'
        response['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS'
        response['Access-Control-Max-Age'] = 1000
        # note that '*' is not valid for Access-Control-Allow-Headers
        response['Access-Control-Allow-Headers'] = 'origin, x-csrftoken, content-type, accept'
        return response
    if request.method == "POST":
        # ... 

Изменить: кажется, что, по крайней мере, в некоторых случаях вам также необходимо добавить те же заголовки Access-Control к фактическому ответу. Это может немного сбивать с толку, так как запрос кажется успешным, но Firefox не передает содержимое ответа в Javascript.


Ваше редактирование о фактическом ответе POST / GET немного страшно; Если кто-то может это подтвердить, пожалуйста, сообщите нам об этом здесь!
Арджан

Я не знаю, является ли это ошибкой или особенностью, но, кажется, что кто-то еще заметил это. Смотрите, например, kodemaniak.de/?p=62 и ищите «пустое тело ответа»
Юха Паломяки

2
Есть разница между простыми запросами и теми, которые требуют предварительной проверки. Ваше «решение» будет работать только с предварительными запросами, поэтому оно не является реальным решением. Всякий раз, когда вы получаете заголовок «Origin:» в заголовках запроса, вы должны отвечать разрешенным.
Одино - Велмонт

1
Я считаю, что заголовок Access-Control-Allow-Headersдолжен содержать значение x-csrf-token, а не x-csrftoken.
JellicleCat

16

В этой статье центра разработчиков Mozilla описаны различные сценарии междоменных запросов. Кажется, в статье указывается, что POST-запрос с типом контента «application / x-www-form-urlencoded» следует отправлять как «простой запрос» (без запроса «preflight» OPTIONS). Однако я обнаружил, что Firefox отправил запрос OPTIONS, хотя мой POST был отправлен с этим типом содержимого.

Я смог сделать эту работу, создав на сервере обработчик запросов опций, который установил для заголовка ответа «Access-Control-Allow-Origin» значение «*». Вы можете быть более строгими, установив для него что-то конкретное, например « http://someurl.com ». Кроме того, я читал, что, предположительно, вы можете указать разделенный запятыми список нескольких источников, но я не мог заставить это работать.

Как только Firefox получает ответ на запрос OPTIONS с приемлемым значением «Access-Control-Allow-Origin», он отправляет запрос POST.


15

Я исправил эту проблему, используя полностью основанное на Apache решение. В моем vhost / htaccess я поставил следующий блок:

# enable cross domain access control
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS"

# force apache to return 200 without executing my scripts
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* / [R=200,L]

Возможно, вам не понадобится последняя часть, в зависимости от того, что происходит, когда Apache выполняет ваш целевой скрипт. Кредит идет к дружественному народу ServerFault для последней части.


Ваш ответ помог мне, но если нужно немного логики за CORS, это не решает полностью.
Ратата Тата

10

Этот PHP в верхней части отвечающего скрипта, кажется, работает. (В Firefox 3.6.11 я еще не проводил много испытаний.)

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Max-Age: 1000');
if(array_key_exists('HTTP_ACCESS_CONTROL_REQUEST_HEADERS', $_SERVER)) {
    header('Access-Control-Allow-Headers: '
           . $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']);
} else {
    header('Access-Control-Allow-Headers: *');
}

if("OPTIONS" == $_SERVER['REQUEST_METHOD']) {
    exit(0);
}

Это может быть дело вкуса, но всегда посылает эти заголовки ответа (также GET, POST...) немного слишком много по своему вкусу. (И мне интересно, всегда ли отправка этих соответствует спецификациям?)
Арьян

3
оберните его, если ($ _ SERVER ['HTTP_ORIGIN']). Если этот заголовок есть, это CORS-запрос, если нет, ну, не нужно ничего отправлять.
Одино - Велмонт

7

У меня была такая же проблема с отправкой запросов на карты Google, и решение довольно просто с jQuery 1.5 - для использования dataType dataType: "jsonp"


12
Несовместимо с методом POST.
Павел Власов

1
Он работает с методом GET, но это очень ограниченное решение. Например, таким образом вы не можете отправить ответ с определенным заголовком, включая токен.
svassr

6

Culprit - предполетный запрос с использованием метода OPTIONS

Для методов HTTP-запроса, которые могут вызывать побочные эффекты для пользовательских данных (в частности, для HTTP-методов, отличных от GET, или для использования POST с определенными типами MIME), спецификация предписывает браузерам «предварительно выполнить» запрос, запрашивая поддерживаемые методы из сервер с методом запроса HTTP OPTIONS, а затем, после «одобрения» от сервера, отправляет фактический запрос с фактическим методом HTTP-запроса.

Веб-спецификация относится к: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Я решил проблему, добавив следующие строки в Nginx conf.

    location / {
               if ($request_method = OPTIONS ) {
                   add_header Access-Control-Allow-Origin  "*";
                   add_header Access-Control-Allow-Methods "POST, GET, PUT, UPDATE, DELETE, OPTIONS";
                   add_header Access-Control-Allow-Headers "Authorization";
                   add_header Access-Control-Allow-Credentials  "true";
                   add_header Content-Length 0;
                   add_header Content-Type text/plain;
                   return 200;
               }
    location ~ ^/(xxxx)$ {
                if ($request_method = OPTIONS) {
                    rewrite ^(.*)$ / last;
                }
    }

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

4

Я просматривал источник 1.3.2, при использовании JSONP запрос делается путем динамического построения элемента SCRIPT, который проходит мимо политики браузеров Same-domain. Естественно, вы не можете сделать POST-запрос, используя элемент SCRIPT, браузер получит результат, используя GET.

Поскольку вы запрашиваете вызов JSONP, элемент SCRIPT не генерируется, потому что он делает это только тогда, когда для вызова Тип AJAX установлено значение GET.

http://dev.jquery.com/ticket/4690


4

У нас была такая проблема с ASP.Net. Наш IIS возвращал внутреннюю ошибку сервера при попытке выполнить jQuery $.postдля получения некоторого html-контента, поскольку PageHandlerFactory был ограничен, чтобы отвечать только GET,HEAD,POST,DEBUGглаголам. Таким образом, вы можете изменить это ограничение, добавив глагол «OPTIONS» в список или выбрав «All Verbs»

Вы можете изменить это в диспетчере IIS, выбрав свой веб-сайт, затем выбрав «Сопоставления обработчиков», дважды щелкнув в вашем PageHandlerFactory для файлов * .apx по мере необходимости (мы используем интегрированный пул приложений со структурой 4.0). Нажмите Запрос ограничений, затем перейдите на вкладку Глаголы и примените ваши изменения.

Теперь наш $.postзапрос работает как положено :)


2

Проверьте, actionсодержит ли URL-адрес формы wwwчасть домена, а открытую исходную страницу просматривают без нее www.

Обычно делается для канонических URL.

Я боролся в течение нескольких часов, прежде чем наткнулся на эту статью и нашел намек на Cross Domain.


2

Мне кажется, что если o.url = 'index.php'и этот файл существует в порядке и возвращает сообщение об успехе в консоли. Он возвращает ошибку, если я использую URL:http://www.google.com

Если вы выполняете почтовый запрос, почему бы не использовать метод $ .post напрямую :

$.post("test.php", { func: "getNameAndTime" },
    function(data){
        alert(data.name); // John
        console.log(data.time); //  2pm
    }, "json");

Это намного проще.


Получил то же самое с этим ... думал, что я должен использовать $ .ajax (), чтобы я мог по крайней мере получить некоторую отладочную информацию об условии ошибки ..
fitzgeraldsteele

1

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

Как отправить междоменный запрос POST через JavaScript?


1

Решение этой проблемы:

  1. использовать dataType: json
  2. добавить &callback=?в свой URL

это сработало при вызове Facebook API и Firefox. Firebug использует GETвместо OPTIONSвышеуказанных условий (оба).




0

Попробуйте добавить опцию:

dataType: "json"


2
что сработало, почему json считается "безопасным" для междоменных запросов?
Ник Так

0
 function test_success(page,name,id,divname,str)
{ 
 var dropdownIndex = document.getElementById(name).selectedIndex;
 var dropdownValue = document.getElementById(name)[dropdownIndex].value;
 var params='&'+id+'='+dropdownValue+'&'+str;
 //makerequest_sp(url, params, divid1);

 $.ajax({
    url: page,
    type: "post",
    data: params,
    // callback handler that will be called on success
    success: function(response, textStatus, jqXHR){
        // log a message to the console
        document.getElementById(divname).innerHTML = response;

        var retname = 'n_district';
        var dropdownIndex = document.getElementById(retname).selectedIndex;
        var dropdownValue = document.getElementById(retname)[dropdownIndex].value;
        if(dropdownValue >0)
        {
            //alert(dropdownValue);
            document.getElementById('inputname').value = dropdownValue;
        }
        else
        {
            document.getElementById('inputname').value = "00";
        }
        return;
        url2=page2; 
        var params2 = parrams2+'&';
        makerequest_sp(url2, params2, divid2);

     }
});         
}

На вопрос уже отвечали 6 месяцев назад. Как это решить?
Бармар

0

У меня была похожая проблема при попытке использовать API Facebook.

Единственный contentType, который не отправлял запрос Preflighted, казался просто text / plain ... а не остальными параметрами, упомянутыми в mozilla здесь

  • Почему это единственный браузер, который делает это?
  • Почему Facebook не знает и не принимает предварительный запрос?

К вашему сведению: вышеупомянутый документ Moz предполагает, что заголовки X-Lori должны инициировать предварительный запрос ... это не так.


0

Вы должны сделать некоторую работу на стороне сервера. Я вижу, что вы используете PHP на стороне сервера, но решение для веб-приложения .NET здесь: невозможно установить для типа контента значение 'application / json' в jQuery.ajax

Сделайте то же самое в PHP-скрипте, и он будет работать. Просто: при первом запросе браузер запрашивает сервер, разрешено ли отправлять такие данные с таким типом, а второй запрос является правильным / разрешенным.


0

Попробуйте добавить следующее:

dataType: "json",
ContentType: "application/json",
data: JSON.stringify({"method":"getStates", "program":"EXPLORE"}),  

0

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

Перейдите по этой ссылке: используя Mode-Rewrite для проксирования , я добавляю эту строку в мой httpd.conf:

 RewriteRule ^solr/(.*)$ http://ip:8983/solr$1 [P]

Поэтому я могу просто публиковать данные в / solr вместо того, чтобы публиковать данные на http: // ip: 8983 / solr / *. Тогда он будет публиковать данные в том же источнике.


0

У меня уже есть этот код хорошо обрабатывает мою ситуацию cors в php:

header( 'Access-Control-Allow-Origin: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Headers: '.CMSConfig::ALLOW_DOMAIN );
header( 'Access-Control-Allow-Credentials: true' );

И он работал нормально локально и удаленно, но не для загрузки, когда удаленно.

Что-то случилось с apache / php ИЛИ моим кодом, я не стал искать его, когда вы запрашиваете OPTIONS, он возвращает мой заголовок с правилами cors, но с результатом 302. Поэтому мой браузер не распознает как приемлемую ситуацию.

Что я сделал, основываясь на ответе @Mark McDonald, просто поместил этот код после моего заголовка:

if( $_SERVER['REQUEST_METHOD'] === 'OPTIONS' )
{
    header("HTTP/1.1 202 Accepted");
    exit;
}

Теперь при запросе OPTIONSон просто отправит заголовок и 202 результата.


-1

Пожалуйста, имейте в виду:

JSONP поддерживает только метод запроса GET.

* Отправить запрос по Firefox : *

$.ajax({
   type: 'POST',//<<===
   contentType: 'application/json',
   url: url,
   dataType: "json"//<<=============
    ...
});

Выше запрос, отправленный с помощью OPTIONS (while ==> введите: 'POST' ) !!!!

$.ajax({
    type: 'POST',//<<===
    contentType: 'application/json',
    url: url,
    dataType: "jsonp"//<<==============
    ...
});

Но выше запрос отправляется GET (while ==> введите: 'POST' ) !!!!

Когда вы находитесь в «междоменной связи», будьте внимательны и осторожны.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.