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 GMT
Сервер: Apache / 2.0.59 (Unix) PHP / 5.2.6 DAV / 2
X-Powered-By: PHP / 5.2.6
Зміст довжини 46
Тайм-аут збереження-тривалості = 15, макс = 100
З'єднання Keep-Alive
Вміст-тип тексту / html

Запити заголовків
Форма замовлення господаря: 8888
User-agent 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
Accept-Encoding gzip, deflate
Accept-Charset ISO-8859-1, utf-8; q = 0.7, *; q = 0.7
Тримайте-живі 300
З'єднання залишається живим
Походження http://ux.inetu.act.org
Метод доступу-контроль-запит POST
Access-Control-Request-заголовки x-просили-з

Ось малюнок виходу Firebug:


Чи можете ви опублікувати відповідь пожежної програми та запити заголовків. Я не отримую жодної помилки, коли запускаю подібний код у Firefox.
MitMaro

Додано інформацію заголовка та зображення з Firebug.
fitzgeraldsteele

Якраз була ця сама проблема під час впровадження вбудованого веб-сервера. Дякуємо за запитання :)
Роберт Гулд

Якщо ви шукаєте рішення Java JAX-RS, дивіться тут: Access-Control-Allow-Origin
Tobias Sarnow

Здається, що поведінка Firefox зараз змінилася? Я не отримую жодних запитів на вибір.
Buge

Відповіді:


169

Причиною помилки є та сама політика походження. Це дозволяє лише робити XMLHTTPRequests до власного домену. Перевірте, чи можете ви замість цього використовувати зворотний дзвінок JSONP :

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

26
Чому Firefox є єдиним браузером для цього? Я хочу, щоб посаду не отримували.
Маслоу

11
Crossite-POST: Хтось знає рішення зробити POST із застосунком / json як Content-Type?
Шетбі

13
То яке саме рішення?
Nik So

3
Шукаю рішення і для цього, а також використовувати getJSON замість виклику ajax, це не робить для мене, оскільки це набагато обмеженіше.
Timo Wallenius

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

57

Я використовував наступний код на стороні Django для інтерпретації запиту OPTIONS та для встановлення необхідних заголовків контролю доступу. Після цього мої міждоменні запити від 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":
        # ... 

Редагувати: схоже, що принаймні в деяких випадках вам також потрібно додати ті самі заголовки контролю доступу до фактичної відповіді. Це може бути трохи заплутано, оскільки запит здається успішним, але Firefox не передає вміст відповіді на Javascript.


Ваша редакція щодо фактичної відповіді POST / GET трохи страшна; якщо хтось може це підтвердити, будь ласка, повідомте нас про це!
Ар'ян

Я не знаю, чи це помилка чи особливість, але здається, що хтось ще помітив це. Дивіться, наприклад, kodemaniak.de/?p=62 та шукайте "порожній орган відповіді"
Juha Palomäki

2
Існує відмінність між простими запитами та тими, які потребують попереднього перельоту. Ваше "рішення" працюватиме лише з попереднім запитом перед польотом, тому це не реальне рішення. Щоразу, коли ви отримуєте заголовок "Origin:" у заголовках запиту, вам слід відповідати, дозволено.
odinho - Велмонт

1
Я вважаю, що заголовок Access-Control-Allow-Headersповинен містити значення x-csrf-token, а не x-csrftoken.
JellicleCat

16

Ця стаття центру розробника mozilla описує різні сценарії запитів між доменними. Стаття, схоже, вказує на те, що запит POST з типом вмісту "application / x-www-form-urlencoded" повинен бути надісланий як "простий запит" (без запиту 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-запит, якщо ні, ну не потрібно нічого надсилати.
odinho - Велмонт

7

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


12
Несумісний із методом POST.
Павло Власов

1
Він працює за методом GET, але це дуже обмежене рішення. Наприклад, таким чином ви не зможете надіслати відповідь із конкретним заголовком, включаючи маркер.
svassr

6

Винуватцем є запит перед польотом за допомогою методу OPTIONS

Для методів запиту HTTP, які можуть спричинити побічні ефекти щодо даних користувачів (зокрема, для методів HTTP, відмінних від GET, або для використання POST з певними типами MIME), специфікація вимагає, щоб браузери "переглядали" запит, вимагаючи підтримуваних методів із сервер з методом запиту HTTP OPTIONS, а потім, після "затвердження" з сервера, відправлення фактичного запиту з фактичним методом запиту HTTP.

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

Я вирішив проблему, додавши наступні рядки в конф. Nginx.

    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 динамічно, який проходить через політику веб-переглядачів одного домену. Звичайно, ви не можете зробити запит POST за допомогою елемента SCRIPT, браузер отримає результат за допомогою GET.

Коли ви запитуєте виклик JSONP, елемент SCRIPT не генерується, тому що він робить це лише тоді, коли для типу виклику AJAX встановлено значення GET.

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


3

У нас була така проблема з ASP.Net. Наш IIS повертав внутрішню помилку сервера під час спроби виконати jQuery, $.postщоб отримати якийсь html-вміст через PageHandlerFactory, було обмежено реагувати лише на GET,HEAD,POST,DEBUGдієслова. Таким чином, ви можете змінити це обмеження, додавши до списку дієслово "ВАРІАНТИ" або вибравши "Усі дієслова"

Ви можете змінити це у своєму менеджері IIS, вибравши свій веб-сайт, потім вибравши відображення обробника, двічі клацніть на вашому PageHandlerFactory для * .apx файлів, як вам потрібно (ми використовуємо Інтегрований пул прикладних програм з Framework 4.0). Клацніть на Запити обмежень, потім перейдіть на вкладку «Дієслова» та застосуйте свою модифікацію.

Зараз наш $.postзапит працює як очікується :)


2

Перевірте, чи actionмістить URL вашої форми wwwчастина домену, а початкова сторінка, яку ви відкрили, переглядається без www.

Зазвичай робиться для канонічних URL-адрес ..

Я боровся годинами, перш ніж натрапити на цю статтю і виявив натяк на Cross Cross.


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

Вирішенням цього є:

  1. використовувати dataType: json
  2. додати &callback=?до URL-адреси

це працювало над тим, щоб зателефонувати в API API та з Firefox. Firebug використовує GETзамість OPTIONSвищезазначених умов (обидва).




0

Спробуйте додати параметр:

dataType: "json"


2
це спрацювало, чому json вважається "безпечним" для міждоменних запитів?
Nik So

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, який не надсилав попередньо просвічений запит, здавався, просто текст / простий ... не решта параметрів, згаданих тут у mozilla

  • Чому це єдиний браузер, який робить це?
  • Чому Facebook не знає та не приймає запит на передполіт?

FYI: Згаданий вище документ 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 проксі, щоб вирішити подібну проблему, коли я хочу розмістити дані на моєму апаш-солі, розміщеному на іншому сервері. (Це може бути не ідеальною відповіддю, але це вирішує мою проблему.)

Дотримуйтесь цієї URL-адреси: Використовуючи Mode-Rewrite для проксі , я додаю цей рядок до свого httpd.conf:

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

Тому я можу просто розміщувати дані в / solr, а не публікувати дані на http: // ip: 8983 / solr / *. Тоді вони будуть розміщувати дані того самого походження.


0

Я вже маю цей код добре обробляти свою ситуацію зі спільним колом у 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 АБО моїм кодом, я не намагався його шукати, коли ви запитуєте ОПЦІЇ, він повертає мій заголовок з правилами 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"//<<=============
    ...
});

Вище звернення надсилайте ВАРІАНТИ (while ==> type: 'POST' ) !!!!

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

Але вище запит надішліть GET (while ==> type: 'POST' ) !!!!

Коли ви перебуваєте в "міждоменному спілкуванні", зверніть увагу і будьте обережні.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.