jQuery $.ajax(), $.post отправляет "OPTIONS" как REQUEST_METHOD в Firefox
Возникли проблемы с тем, что я считал относительно простым плагином 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 GMT Сервер: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:
23 ответа
Причиной ошибки является та же политика происхождения. Это только позволяет вам делать запросы XMLHTTP к вашему собственному домену. Посмотрите, можете ли вы использовать обратный вызов JSONP:
$.getJSON( 'http://<url>/api.php?callback=?', function ( data ) { alert ( data ); } );
Я использовал следующий код на стороне 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.
В этой статье центра разработчиков 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.
Я исправил эту проблему, используя полностью основанное на 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 для последней части.
Этот 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);
}
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;
}
}
У меня была такая же проблема с отправкой запросов на карты Google, и решение довольно просто с jQuery 1.5 - для использования dataType dataType: "jsonp"
У нас была такая проблема с ASP.Net. Наш IIS возвращал внутреннюю ошибку сервера при попытке выполнить jQuery $.post
чтобы получить некоторое содержание HTML из-за PageHandlerFactory было запрещено отвечать только GET,HEAD,POST,DEBUG
Глаголы. Таким образом, вы можете изменить это ограничение, добавив глагол "OPTIONS" в список или выбрав "All Verbs"
Вы можете изменить это в диспетчере IIS, выбрав свой веб-сайт, затем выбрав "Сопоставления обработчиков", дважды щелкнув в вашем PageHandlerFactory для файлов *.apx, как вам нужно (мы используем интегрированный пул приложений со структурой 4.0). Нажмите Запрос ограничений, затем перейдите на вкладку Глаголы и примените ваши изменения.
Теперь наш $.post
запрос работает как положено:)
Я просматривал источник 1.3.2, когда при использовании JSONP запрос выполнялся путем динамического построения элемента SCRIPT, который проходит мимо политики браузера Same-domain. Естественно, вы не можете сделать POST-запрос, используя элемент SCRIPT, браузер получит результат, используя GET.
Поскольку вы запрашиваете вызов JSONP, элемент SCRIPT не генерируется, поскольку он делает это только тогда, когда для вызова Тип AJAX установлено значение GET.
Мне кажется что если 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");
Это намного проще.
Проверьте, если ваша форма action
URL включает в себя www
часть домена, а открытая исходная страница просматривается без www
,
Обычно делается для канонических URL.
Я боролся в течение нескольких часов, прежде чем наткнулся на эту статью и нашел намек на Cross Domain.
Решение этой проблемы:
- использовать dataType:
json
- добавлять
&callback=?
на ваш URL
это сработало при вызове Facebook API и Firefox. Firebug использует GET
вместо OPTIONS
с вышеуказанными условиями (оба).
Я опубликовал четкий пример того, как решить эту проблему, если вы управляете кодом сервера домена, на который вы размещаете сообщение. Этот ответ затрагивается в этой теме, но это более четко объясняет его IMO.
Другая возможность обойти проблему - использовать прокси-скрипт. Этот метод описан, например, здесь
У меня была похожая проблема при попытке использовать API Facebook.
Единственный contentType, который не отправлял запрос Preflighted, казался просто text / plain... а не остальными параметрами, упомянутыми здесь в mozilla
- Почему это единственный браузер, который делает это?
- Почему Facebook не знает и не принимает предварительный запрос?
К вашему сведению: вышеупомянутый документ Moz предлагает, что заголовки X-Lori должны инициировать предварительный запрос... это не так.
У меня уже есть этот код хорошо обрабатывает мою ситуацию 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 результата.
Попробуйте добавить следующее:
dataType: "json",
ContentType: "application/json",
data: JSON.stringify({"method":"getStates", "program":"EXPLORE"}),
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);
}
});
}
Можете ли вы попробовать это без
contentType:application/x-www-form-urlencoded
Вы должны сделать некоторую работу на стороне сервера. Я вижу, что вы используете PHP на стороне сервера, но решение для веб-приложения.NET здесь: невозможно установить для типа контента значение 'application/json' в jQuery.ajax
Сделайте то же самое в PHP-скрипте, и он будет работать. Просто: при первом запросе браузер запрашивает у сервера, разрешено ли отправлять такие данные с таким типом, а второй запрос является правильным / разрешенным.
Я использовал URL-адрес прокси-сервера для решения аналогичной проблемы, когда я хочу разместить данные в моем apache solr, размещенном на другом сервере. (Возможно, это не идеальный ответ, но он решает мою проблему.)
Перейдите по этой ссылке: используя Mode-Rewrite для проксирования, я добавляю эту строку в мой httpd.conf:
RewriteRule ^solr/(.*)$ http://ip:8983/solr$1 [P]
Поэтому я могу просто публиковать данные в / solr вместо того, чтобы публиковать данные на http://ip:8983/solr/*. Тогда он будет публиковать данные в том же источнике.
Доводим до вашего сведения:
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')!!!!
Когда вы находитесь в "междоменной связи", будьте внимательны и осторожны.