jQuery $ .ajax (), $ .post mengirim "OPTIONS" sebagai REQUEST_METHOD di Firefox


330

Mengalami masalah dengan apa yang saya pikir adalah plugin jQuery yang relatif sederhana ...

Plugin harus mengambil data dari skrip php melalui ajax untuk menambahkan opsi ke a <select>. Permintaan ajax cukup umum:

$.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);
  }
});

Ini sepertinya berfungsi dengan baik di Safari. Di Firefox 3.5, REQUEST_TYPEpada server selalu 'OPSI', dan data $ _POST tidak muncul. Apache mencatat permintaan sebagai jenis 'OPSI':

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

Mengapa panggilan ajax ini berfungsi di Safari, tetapi bukan Firefox, dan bagaimana cara memperbaikinya untuk Firefox?

Header Respons
Tanggal: Rabu, 08 Jul 2009 21:22:17 GMT
Server: Apache / 2.0.59 (Unix) PHP / 5.2.6 DAV / 2
X-Powered-By: PHP / 5.2.6
Panjang Konten 46
Batas waktu Keep-Alive = 15, maks = 100
Koneksi Tetap Alive
Konten-Jenis teks / html

Tajuk Permintaan
Formulir pesanan host: 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
Terima teks / html, aplikasi / xhtml + xml, aplikasi / xml; q = 0,9, * / *; q = 0,8
Bahasa Terima en-us, en; q = 0,5
Terima-Encoding gzip, deflate
Terima-Charset ISO-8859-1, utf-8; q = 0,7, *; q = 0,7
Keep-Alive 300
Koneksi tetap hidup
Origin http://ux.inetu.act.org
Akses-Kontrol-Permintaan-Metode POST
Akses-Kontrol-Permintaan-Header x-diminta-dengan

Ini adalah gambar hasil Firebug:


Bisakah Anda memposting respons pembakar dan meminta header. Saya tidak mendapatkan kesalahan saat menjalankan kode serupa di Firefox.
MitMaro

Menambahkan info tajuk, dan gambar dari Firebug.
fitzgeraldsteele

Baru saja mengalami masalah yang sama saat menerapkan server web tertanam. Terima kasih telah bertanya :)
Robert Gould

Jika Anda mencari solusi Java JAX-RS, lihat di sini: Access-Control-Allow-Origin
Tobias Sarnow

Perilaku firefox tampaknya telah berubah sekarang? Saya tidak mendapatkan permintaan opsi apa pun.
Buge

Jawaban:


169

Alasan kesalahan adalah kebijakan asal yang sama. Ini hanya memungkinkan Anda untuk melakukan XMLHTTPRequests ke domain Anda sendiri. Lihat apakah Anda dapat menggunakan panggilan balik JSONP sebagai gantinya:

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

26
mengapa firefox satu-satunya browser yang melakukan ini? Saya ingin posting bukan get.
Maslow

11
Crossite-POST: Adakah yang tahu solusi untuk melakukan POST dengan aplikasi / json sebagai Content-Type?
schoetbi

13
Jadi apa sebenarnya solusinya?
Nik So

3
Mencari solusi untuk ini juga dan menggunakan getJSON alih-alih panggilan ajax tidak melakukannya untuk saya karena jauh lebih terbatas.
Timo Wallenius

1
@schoetbi untuk itu Anda perlu menggunakan CORS, yang didukung dengan baik di peramban yang lebih baru ... dukungan terbatas di IE8-9, dan memerlukan dukungan sisi server.
Tracker1

57

Saya menggunakan kode berikut di sisi Django untuk menafsirkan permintaan OPSI dan untuk mengatur header Access-Control yang diperlukan. Setelah ini, permintaan lintas domain saya dari Firefox mulai berfungsi. Seperti yang dikatakan sebelumnya, browser terlebih dahulu mengirimkan permintaan PILIHAN dan kemudian segera setelah itu 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":
        # ... 

Sunting: tampaknya setidaknya dalam beberapa kasus Anda juga perlu menambahkan header Access-Control yang sama ke respons aktual. Ini bisa sedikit membingungkan, karena permintaan tampaknya berhasil, tetapi Firefox tidak meneruskan konten tanggapan ke Javascript.


Hasil edit Anda tentang respons POST / GET yang sebenarnya agak menakutkan; jika ada yang bisa mengkonfirmasi itu, silakan beri tahu kami di sini!
Arjan

Saya tidak tahu apakah itu bug atau fitur, tetapi tampaknya orang lain juga memperhatikannya. Lihat misalnya kodemaniak.de/?p=62 dan cari "badan tanggapan kosong"
Juha Palomäki

2
Ada perbedaan antara permintaan sederhana dan yang membutuhkan preflight. "Solusi" Anda hanya akan berfungsi dengan permintaan preflight, jadi itu bukan solusi nyata. Setiap kali Anda mendapatkan "Asal:" - tajuk di tajuk permintaan, Anda harus membalas dengan yang diizinkan.
odinho

1
Saya percaya header Access-Control-Allow-Headersharus berisi nilainya x-csrf-token, bukan x-csrftoken.
JellicleCat

16

Ini artikel mozilla pusat pengembang menggambarkan berbagai skenario permintaan lintas-domain. Artikel ini tampaknya mengindikasikan bahwa permintaan POST dengan jenis konten 'aplikasi / x-www-form-urlencoded' harus dikirim sebagai 'permintaan sederhana' (tanpa permintaan OPSI 'preflight' OPSI). Namun, saya menemukan bahwa Firefox mengirim permintaan PILIHAN, meskipun POST saya dikirim dengan jenis konten itu.

Saya dapat membuat pekerjaan ini dengan membuat penangan permintaan opsi di server, yang mengatur header respons 'Akses-Kontrol-Bolehkan-Asal' ke '*'. Anda dapat lebih membatasi dengan mengaturnya ke sesuatu yang spesifik, seperti ' http://someurl.com '. Juga, saya telah membaca bahwa, konon, Anda dapat menentukan daftar asal beberapa koma, tapi saya tidak bisa membuatnya bekerja.

Setelah Firefox menerima respons atas permintaan OPTIONS dengan nilai 'Akses-Kontrol-Bolehkan-Asal' yang dapat diterima, ia mengirim permintaan POST.


15

Saya telah memperbaiki masalah ini menggunakan solusi sepenuhnya berbasis Apache. Di vhost / htaccess saya, saya meletakkan blok berikut:

# 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]

Anda mungkin tidak memerlukan bagian terakhir, tergantung pada apa yang terjadi ketika Apache mengeksekusi skrip target Anda. Kredit jatuh ke rakyat ServerFault yang ramah untuk bagian yang terakhir.


Jawaban Anda membantu saya, tetapi jika perlu logika di balik CORS, itu tidak menyelesaikan sepenuhnya.
Ratata Tata

10

PHP di bagian atas skrip yang merespons tampaknya berfungsi. (Dengan Firefox 3.6.11. Saya belum melakukan banyak pengujian.)

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);
}

Ini mungkin masalah selera, tapi selalu mengirim orang-header respon (juga untuk GET, POST, ...) agak terlalu banyak untuk saya sukai. (Dan, saya ingin tahu apakah selalu mengirim yang sesuai dengan spesifikasi?)
Arjan

3
bungkus dalam if ($ _ SERVER ['HTTP_ORIGIN']). Jika tajuk itu ada, itu adalah permintaan CORS, jika tidak, yah, tidak perlu mengirim apa pun.
odinho

7

Saya punya masalah yang sama dengan mengirim permintaan ke google maps, dan solusinya cukup sederhana dengan jQuery 1.5 - untuk penggunaan dataType dataType: "jsonp"


12
Tidak kompatibel dengan metode POST.
Pavel Vlasov

1
Ini bekerja dengan metode GET tetapi itu solusi yang sangat terbatas. Misalnya, dengan melakukan itu, Anda tidak dapat mengirim kembali respons dengan tajuk tertentu termasuk token.
svassr

6

Pelakunya adalah permintaan preflight menggunakan metode OPSI

Untuk metode permintaan HTTP yang dapat menyebabkan efek samping pada data pengguna (khususnya, untuk metode HTTP selain GET, atau untuk penggunaan POST dengan tipe MIME tertentu), mandat spesifikasi yang diminta browser "mengatur terlebih dahulu" permintaan, meminta metode yang didukung dari server dengan metode permintaan HTTP OPTIONS, dan kemudian, setelah "persetujuan" dari server, mengirimkan permintaan aktual dengan metode permintaan HTTP yang sebenarnya.

Spesifikasi web merujuk ke: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Saya menyelesaikan masalah dengan menambahkan baris berikut di 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
Jawaban ini sangat membantu terima kasih. Fakta bahwa browser mengirim permintaan preflight dengan metode OPSI tidak jelas.
Normangorman

4

Saya mencari melalui sumber 1.3.2, ketika menggunakan JSONP, permintaan dibuat dengan membangun elemen SCRIPT secara dinamis, yang melewati kebijakan Same-domain browser. Secara alami, Anda tidak dapat membuat permintaan POST menggunakan elemen SCRIPT, browser akan mengambil hasilnya menggunakan GET.

Saat Anda meminta panggilan JSONP, elemen SCRIPT tidak dibuat, karena hanya melakukan ini ketika Tipe panggilan AJAX diatur ke GET.

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


4

Kami punya masalah seperti ini dengan ASP.Net. IIS kami mengembalikan Galat Server Internal ketika mencoba menjalankan jQuery $.postuntuk mendapatkan beberapa konten html karena PageHandlerFactory dibatasi hanya untuk merespons sajaGET,HEAD,POST,DEBUG Verb. Jadi, Anda dapat mengubah batasan itu dengan menambahkan kata kerja "PILIHAN" ke daftar atau memilih "Semua Verba"

Anda dapat memodifikasi itu di IIS Manager Anda, memilih situs web Anda, lalu memilih Handler Mappings, klik dua kali pada PageHandlerFactory untuk file * .apx yang Anda butuhkan (Kami menggunakan kumpulan aplikasi terintegrasi dengan kerangka 4.0). Klik pada Batasan Permintaan, lalu buka Verbs Tabn dan terapkan modifikasi Anda.

Sekarang $.postpermintaan kami berfungsi seperti yang diharapkan :)


2

Periksa apakah actionURL formulir Anda termasuk wwwbagian dari domain, sementara halaman asli yang Anda buka dilihat tanpa www.

Biasanya dilakukan untuk Url Canonical ..

Saya berjuang selama berjam-jam sebelum menemukan artikel ini dan menemukan petunjuk Cross Domain.


2

Saya tampaknya jika o.url = 'index.php'dan file ini ada, ok dan mengembalikan pesan sukses di konsol. Ini mengembalikan kesalahan jika saya menggunakan url:http://www.google.com

Jika melakukan permintaan posting mengapa tidak menggunakan langsung metode $ .post :

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

Itu jauh lebih sederhana.


Mendapat hal yang sama dengan ini ... pikir saya harus menggunakan $ ajax () jadi saya bisa setidaknya mendapatkan beberapa info debug pada kondisi kesalahan ..
fitzgeraldsteele


1

Solusi untuk ini adalah:

  1. gunakan tipe data: json
  2. tambahkan &callback=?ke url Anda

ini berfungsi untuk memanggil Facebook API dan dengan Firefox. Firebug menggunakan GETalih-alih OPTIONSdengan kondisi di atas (keduanya).




0

Coba tambahkan opsi:

dataType: "json"


2
yang berhasil, mengapa json dianggap "aman" untuk permintaan lintas domain?
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);

     }
});         
}

Pertanyaan itu sudah dijawab 6 bulan lalu. Bagaimana cara mengatasinya?
Barmar

0

Saya memiliki masalah serupa dengan mencoba menggunakan API Facebook.

Satu-satunya contentType yang tidak mengirim permintaan Preflighted tampaknya hanya teks / polos ... bukan parameter lainnya yang disebutkan di mozilla di sini

  • Mengapa ini satu-satunya browser yang melakukan ini?
  • Mengapa Facebook tidak tahu dan menerima permintaan preflight?

FYI: Moz doc yang disebutkan di atas menyarankan header X-Lori harus memicu permintaan Preflighted ... tidak.


0

Anda perlu melakukan beberapa pekerjaan di sisi server. Saya melihat Anda menggunakan PHP di sisi server, tetapi solusi untuk aplikasi web .NET ada di sini: Tidak dapat mengatur tipe konten ke 'application / json' di jQuery.ajax

Lakukan hal yang sama dalam skrip PHP dan itu akan berfungsi. Cukup: Pada permintaan pertama browser menanyakan server apakah boleh mengirim data dengan tipe seperti itu dan permintaan kedua adalah yang tepat / diizinkan.


0

Coba tambahkan yang berikut ini:

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

0

Saya menggunakan url proxy untuk menyelesaikan masalah serupa ketika saya ingin memposting data ke apache solr saya yang dihosting di server lain. (Ini mungkin bukan jawaban yang sempurna tetapi itu menyelesaikan masalah saya.)

Ikuti URL ini: Menggunakan Mode-Rewrite untuk proxy , saya menambahkan baris ini ke httpd.conf saya:

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

Oleh karena itu, saya hanya dapat memposting data ke / solr daripada mengirim data ke http: // ip: 8983 / solr / *. Maka itu akan memposting data dalam asal yang sama.


0

Saya sudah memiliki kode ini yang menangani situasi cors saya dengan baik di php:

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

Dan itu berfungsi dengan baik secara lokal dan jarak jauh, tetapi tidak untuk unggahan saat jarak jauh.

Sesuatu terjadi dengan apache / php ATAU kode saya, saya tidak repot-repot untuk mencarinya, ketika Anda meminta OPSI itu mengembalikan header saya dengan aturan cors tetapi dengan hasil 302. Karenanya browser saya tidak mengenali sebagai situasi yang dapat diterima.

Apa yang saya lakukan, berdasarkan jawaban @ Mark McDonald, hanya menaruh kode ini setelah header saya:

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

Sekarang, ketika memintanya OPTIONShanya akan mengirim header dan 202 hasilnya.


-1

Harap maklum:

JSONP hanya mendukung metode permintaan GET.

* Kirim permintaan oleh firefox : *

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

Permintaan di atas kirim melalui OPSI (sambil ==> ketik: 'POST' ) !!!!

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

Tetapi permintaan di atas kirim dengan GET (selagi ==> ketik: 'POST' ) !!!!

Saat Anda berada di "komunikasi lintas domain", perhatikan dan berhati-hatilah.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.