AJAX di Chrome mengirimkan OPTIONS alih-alih GET / POST / PUT / DELETE?

107

Saya sedang mengerjakan aplikasi web internal di tempat kerja. Di IE10 permintaan berfungsi dengan baik, tetapi di Chrome semua permintaan AJAX (yang ada banyak) dikirim menggunakan OPSI alih-alih metode apa pun yang saya berikan. Secara teknis, permintaan saya adalah "lintas domain". Situs ini disajikan di localhost: 6120 dan layanan yang saya minta dari AJAX ada di 57124. Bug jquery tertutup ini mendefinisikan masalah, tetapi bukan perbaikan yang sebenarnya.

Apa yang dapat saya lakukan untuk menggunakan metode http yang tepat dalam permintaan ajax?

Edit:

Ini ada di pemuatan dokumen di setiap halaman:

jQuery.support.cors = true;

Dan setiap AJAX dibangun dengan cara yang sama:

var url = 'http://localhost:57124/My/Rest/Call';
$.ajax({
    url: url,
    dataType: "json",
    data: json,
    async: true,
    cache: false,
    timeout: 30000,
    headers: { "x-li-format": "json", "X-UserName": userName },
    success: function (data) {
        // my success stuff
    },
    error: function (request, status, error) {
        // my error stuff
    },
    type: "POST"
});
Corey Ogburn
sumber
2
Komentar terakhir dalam laporan bug menjelaskannya dengan cukup baik ...
Kevin B
1
Itu membalikkan pikiran saya karena semua yang saya lakukan sangat vanilla (dan kode saya mirip dengan yang ada di bug jquery). Selain itu, tidak ada alasan untuk tidak memasukkannya. BRB, mengambil beberapa kode contoh.
Corey Ogburn
3
Perhatikan bahwa IE tidak mempertimbangkan nomor port saat menentukan apakah permintaan lintas sumber.
Ray Nicholus
@KevinB: Layanan REST kami memanfaatkan permintaan yang berbeda karena melakukan hal yang berbeda berdasarkan metode http. Mengalihkan semuanya ke GET bukanlah perbaikan yang valid. Juga, menurut jawaban Dark Falcon, itu tidak akan membantu karena saya memiliki X-UserName dan tajuk khusus lainnya dalam permintaan.
Corey Ogburn
yang tidak mengubah fakta bahwa jika Anda ingin membuat permintaan lintas sumber, Anda harus mengikuti semua aturan yang berlaku untuk permintaan lintas sumber agar berfungsi dengan benar. permintaan lintas sumber biasanya melibatkan permintaan OPTIONS. Tangani dengan benar dan masalahnya akan hilang. Satu-satunya cara lain untuk mengatasi ini (tanpa mengubah api) adalah dengan memiliki skrip di server yang sama dengan halaman utama yang berinteraksi dengan api menggunakan kode sisi server.
Kevin B

Jawaban:

136

Chrome melakukan pra-penerbangan permintaan untuk mencari header CORS . Jika permintaan diterima, permintaan sebenarnya akan dikirim. Jika Anda melakukan lintas-domain ini, Anda hanya perlu menanganinya atau mencari cara untuk membuat permintaan non-lintas-domain. Inilah sebabnya mengapa bug jQuery ditutup karena tidak dapat diperbaiki. Ini memang disengaja.

Tidak seperti permintaan sederhana (dibahas di atas), permintaan "preflighted" pertama-tama mengirim permintaan HTTP dengan metode OPTIONS ke sumber daya di domain lain, untuk menentukan apakah permintaan yang sebenarnya aman untuk dikirim. Permintaan lintas situs diatur sebelumnya seperti ini karena mungkin berdampak pada data pengguna. Secara khusus, permintaan dipraflight jika:

  • Ini menggunakan metode selain GET, HEAD atau POST. Selain itu, jika POST digunakan untuk mengirim data permintaan dengan Jenis Konten selain application / x-www-form-urlencoded, multipart / form-data, atau text / plain, misalnya jika permintaan POST mengirimkan payload XML ke server menggunakan application / xml atau text / xml, maka permintaan tersebut telah ditetapkan sebelumnya.
  • Ini menetapkan tajuk khusus dalam permintaan (misalnya, permintaan menggunakan tajuk seperti X-PINGOTHER)
Falcon Gelap
sumber
20
Header kustom. Mungkin itulah yang memicu panggilan OPTIONS sebelum penerbangan.
Corey Ogburn
18

Berdasarkan fakta bahwa permintaan tidak dikirim pada port default 80/443 panggilan Ajax ini secara otomatis dianggap sebagai permintaan sumber daya lintas sumber (CORS) , yang dengan kata lain berarti bahwa permintaan tersebut secara otomatis mengeluarkan permintaan OPTIONS yang memeriksa Header CORS di sisi server / servlet.

Ini terjadi bahkan jika Anda menyetelnya

crossOrigin: false;

atau bahkan jika Anda mengabaikannya.

Alasannya sederhana localhost != localhost:57124. Coba kirimkan hanya ke localhosttanpa port - itu akan gagal, karena target yang diminta tidak dapat dijangkau, namun perhatikan bahwa jika nama domain sama , permintaan dikirim tanpa permintaan OPTIONS sebelum POST.

Keluar
sumber
3

Saya setuju dengan Kevin B, laporan bug menjelaskan semuanya. Sepertinya Anda mencoba melakukan panggilan ajax lintas domain. Jika Anda tidak terbiasa dengan kebijakan origin yang sama, Anda dapat memulai di sini: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript .

Jika ini tidak dimaksudkan untuk menjadi panggilan ajax lintas domain, coba buat url target Anda relatif dan lihat apakah masalahnya hilang. Jika Anda benar-benar putus asa, lihat JSONP, tetapi waspadalah, kekacauan mengintai. Tidak banyak lagi yang bisa kami lakukan untuk membantu Anda.

jgitter
sumber
1
Struktur sistem kami adalah sesuatu yang tidak dapat saya ubah. Menggunakan port yang berbeda merupakan persyaratan arsitektur kami. Saya mendapatkan kebijakan asal yang sama tetapi berpikir bahwa CORS yang kami terapkan sudah cukup. Sepertinya tidak.
Corey Ogburn
2
Jika server Anda mengembalikan respons JSON, Anda dapat melihat metode JSONP, gunakan saja secara bertanggung jawab.
jgitter
1
Saya tidak terlalu peduli untuk berdebat dengan Anda, tetapi JSONP menggunakan tag skrip untuk menarik data dari domain lain dan kemudian mengirimkan hasilnya ke fungsi panggilan balik. Jauh lebih sulit jika hasilnya bukan json.
jgitter
1
Tidak, itu tidak lebih sulit. Faktanya, respons tersebut seharusnya tidak berupa JSON yang valid. Sebaliknya, server harus kembali sesuatu seperti ini: callbackfunc(somedata). Seperti yang Anda lihat, ini bukan JSON yang valid. Dan, somedatabisa berupa string, atau angka, atau apa pun yang Anda inginkan.
Ray Nicholus
1
Saya menggunakan Postman dan di sana metode permintaan dikirim dengan benar (mis. 'PUT', 'DELETE', dll). Tetapi ketika saya mencoba melakukannya dari kode saya, itu selalu mengirim mereka dengan metode permintaan OPTIONS. Saya tidak tahu bagaimana Postman dapat melakukannya.
ErwinGO
1

Jika memungkinkan, berikan parameter melalui GET / POST biasa dengan nama yang berbeda dan biarkan kode sisi server Anda menanganinya.

Saya memiliki masalah serupa dengan proxy saya sendiri untuk melewati CORS dan saya mendapatkan kesalahan yang sama dari POST-> OPTION di Chrome. Itu adalah Authorizationtajuk dalam kasus saya ( "x-li-format"dan "X-UserName"di sini dalam kasus Anda.) Saya akhirnya meneruskannya dalam format dummy (misalnya AuthorizatinJackdalam GET) dan saya mengubah kode untuk proxy saya untuk mengubahnya menjadi tajuk saat melakukan panggilan ke tujuan . Ini dia di PHP:

if (isset($_GET['AuthorizationJack'])) {
    $request_headers[] = "Authorization: Basic ".$_GET['AuthorizationJack'];
}
Aidin
sumber
1

Dalam kasus saya, saya memanggil API yang dihosting oleh AWS (API Gateway). Kesalahan terjadi saat saya mencoba memanggil API dari domain selain domain API sendiri. Karena saya adalah pemilik API, saya mengaktifkan CORS untuk lingkungan pengujian, seperti yang dijelaskan dalam Dokumentasi Amazon .

Dalam produksi kesalahan ini tidak akan terjadi, karena permintaan dan api akan berada di domain yang sama.

Saya harap ini membantu!

gbonesso.dll
sumber
0

Seperti yang dijawab oleh @Dark Falcon, saya hanya menanganinya .

Dalam kasus saya, saya menggunakan server node.js, dan membuat sesi jika tidak ada. Karena metode OPTIONS tidak memiliki detail sesi di dalamnya, metode ini akhirnya membuat sesi baru untuk setiap permintaan metode POST.

Jadi dalam rutinitas aplikasi saya untuk membuat-sesi-jika-tidak-ada, saya hanya menambahkan tanda centang untuk melihat apakah metodenya OPTIONS, dan jika demikian, lewati saja sesi pembuatan bagian:

    app.use(function(req, res, next) {
        if (req.method !== "OPTIONS") {
            if (req.session && req.session.id) {
                 // Session exists
                 next();
            }else{
                 // Create session
                 next();
          }
        } else {
           // If request method is OPTIONS, just skip this part and move to the next method.
           next(); 
        }
    }
Mahesh
sumber
0

Permintaan "preflighted" pertama-tama mengirim permintaan HTTP dengan metode OPTIONS ke sumber daya di domain lain, untuk menentukan apakah permintaan sebenarnya aman untuk dikirim. Permintaan lintas situs

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

Noorullah
sumber
1
Bisakah Anda menambahkan lebih banyak informasi? Jawaban Anda terlihat seperti komentar. :)
Badacadabra
0

Pertimbangkan untuk menggunakan aksios

axios.get( url,
{ headers: {"Content-Type": "application/json"} } ).then( res => {

  if(res.data.error) {

  } else { 
    doAnything( res.data )
  }

}).catch(function (error) {
   doAnythingError(error)
});

Saya mengalami masalah ini menggunakan fetch dan axios bekerja dengan sempurna.

Evhz
sumber
5
Axios juga menggunakan OPSI pertama
Skylin R
0

Saya mengalami masalah yang sangat mirip. Saya menghabiskan hampir setengah hari untuk memahami mengapa semuanya bekerja dengan benar di Firefox dan gagal di Chrome. Dalam kasus saya itu karena bidang duplikat (atau mungkin salah ketik) di header permintaan saya.

Andrew Tatomyr
sumber
0

Gunakan fetch sebagai ganti XHR, maka permintaan tidak akan berpandangan sebelumnya meskipun itu lintas domain.

Fei Sun
sumber
-1
 $.ajax({
            url: '###',
            contentType: 'text/plain; charset=utf-8',
            async: false,
            xhrFields: {
                withCredentials: true,
                crossDomain: true,
                Authorization: "Bearer ...."
            },

            method: 'POST',

            data: JSON.stringify( request ),
            success: function (data) {
                console.log(data);
            }
        });

the contentType: 'text / plain; charset = utf-8 ', atau hanya contentType:' text / plain ', cocok untuk saya! salam!!

David Lopes
sumber
Apa hubungannya ini dengan pertanyaan itu?
Corey Ogburn
Hai, saya pikir ini menyelesaikan masalah dalam judul, dengan tipe konten ini Anda melewati metode OPTIONS. Salam
David Lopes
ContentType tidak ada hubungannya dengan metode ini.
Corey Ogburn
Saya tahu apa yang Anda katakan, tetapi cobalah. tergantung browser, jenis konten Anda dapat memengaruhi permintaan Anda dan mengubah Metode Anda!
David Lopes