Mengapa saya mendapatkan permintaan OPSI alih-alih permintaan GET?

288
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js" type="text/javascript"></script>
<script>
$.get("http://example.com/", function(data) {
     alert(data);
});
</script>

itu melakukan permintaan PILIHAN ke URL itu, dan kemudian panggilan balik tidak pernah dipanggil dengan apa pun.

Ketika tidak lintas domain, itu berfungsi dengan baik.

Tidakkah seharusnya jQuery hanya melakukan panggilan dengan sebuah <script>node dan kemudian melakukan panggilan balik saat itu dimuat? Saya mengerti bahwa saya tidak akan bisa mendapatkan hasilnya (karena itu lintas domain), tapi tidak apa-apa; Saya hanya ingin panggilan itu masuk. Apakah ini bug, atau saya melakukan sesuatu yang salah?

Paul Tarjan
sumber
2
Bisa karena domain lintas. Misalnya jika Anda berada di file Anda File: // PATH_TO_WEBSITE daripada menggunakan localhost / WEBSITE_LINK
James111

Jawaban:

262

Menurut MDN ,

Permintaan yang sudah ditentukan sebelumnya

Tidak seperti permintaan sederhana (dibahas di atas), permintaan "preflighted" pertama-tama mengirim tajuk permintaan HTTP OPTIONS ke sumber daya di domain lain, untuk menentukan apakah permintaan sebenarnya aman untuk dikirim. Permintaan lintas-situs telah ditetapkan sebelumnya seperti ini karena mungkin memiliki implikasi pada data pengguna. Secara khusus, permintaan sudah dicetak sebelumnya jika:

  • Ini menggunakan metode selain GET atau POST. Juga, jika POST digunakan untuk mengirim data permintaan dengan Tipe-Konten selain dari aplikasi / x-www-form-urlencoded, multipart / form-data, atau teks / polos, mis. Jika permintaan POST mengirimkan payload XML ke server menggunakan aplikasi / xml atau teks / xml, maka permintaannya sudah disorot.
  • Itu mengatur header kustom dalam permintaan (mis. Permintaan menggunakan header seperti X-PINGOTHER)
arturgrigor
sumber
43
ini memperbaiki masalah kami, mengubah dari "application / json" menjadi "text / plain" menghentikan permintaan opsi mengerikan
Keeno
10
apa yang saya tidak mengerti adalah mengapa browser meminta dengan metode PILIHAN hanya untuk memeriksa permintaan sebenarnya aman untuk dikirim. tetapi dalam arti apa? i mean server juga dapat menempatkan pembatasan dengan header respons tertentu jadi mengapa ini diperlukan?
hardik
11
@hardik Ingatlah bahwa dengan menambahkan CORS, Anda berpotensi menerima permintaan dari siapa pun, di mana mereka dapat memanipulasi data di server Anda melalui permintaan (POST, PUT, DELETE dll). Dalam situasi ini, seperti ketika menggunakan header khusus, browser hanya memeriksa dengan server terlebih dahulu bahwa server bersedia menerima permintaan sebelum mengirimnya karena mengirim permintaan yang tidak diminta ke server bisa sangat berbahaya untuk data Anda, dan juga, apa titik di browser mengirim muatan berpotensi besar jika server tidak ingin menerimanya, maka OPSI pra-penerbangan memeriksa.
davidnknight
6
@davidnknight jika mengirim data Anda ke server bisa berbahaya, artinya server mungkin dikompromikan, maka tentu saja server jahat akan menanggapi permintaan OPSI Anda dengan "Tentu, kirim semuanya!". Bagaimana keamanannya? (pertanyaan jujur)
Matt
3
"Permintaan preflight bukan hal keamanan. Sebaliknya, itu bukan hal yang mengubah aturan." - Lihat jawaban untuk Apa Motivasi Dibalik Memperkenalkan Permintaan
Preflight
9

Jika Anda mencoba POST

Pastikan untuk JSON.stringifydata formulir Anda dan kirim sebagai text/plain.

<form id="my-form" onSubmit="return postMyFormData();">
    <input type="text" name="name" placeholder="Your Name" required>
    <input type="email" name="email" placeholder="Your Email" required>
    <input type="submit" value="Submit My Form">
</form>

function postMyFormData() {

    var formData = $('#my-form').serializeArray();
    formData = formData.reduce(function(obj, item) {
        obj[item.name] = item.value;
        return obj;
    }, {});
    formData = JSON.stringify(formData);

    $.ajax({
        type: "POST",
        url: "https://website.com/path",
        data: formData,
        success: function() { ... },
        dataType: "text",
        contentType : "text/plain"
    });
}
Derek Soike
sumber
2

Saya tidak percaya jQuery secara alami akan melakukan permintaan JSONP ketika diberi URL seperti itu. Namun, itu akan melakukan permintaan JSONP ketika Anda memberi tahu argumen apa yang digunakan untuk panggilan balik:

$.get("http://metaward.com/import/http://metaward.com/u/ptarjan?jsoncallback=?", function(data) {
     alert(data);
});

Semuanya bergantung pada skrip penerima untuk menggunakan argumen itu (yang tidak harus disebut "jsoncallback"), jadi dalam hal ini fungsinya tidak akan pernah dipanggil. Tapi, karena Anda menyatakan ingin mengeksekusi skrip di metaward.com, itu akan membuatnya.

VoteyDisciple
sumber
apakah panggilan balik SAYA masih akan diberitahukan bahwa elemen skrip telah dimuat penuh? Saya hanya ingin memastikan pembaruan terjadi sebelum saya meminta API untuk itu.
Paul Tarjan
Anda akan melakukannya jika skrip penerima mendukung JSONP, dan bersedia memanggil fungsi yang Anda identifikasi. Jika skrip tidak menghasilkan apa-apa selain menghasilkan blok data JSON tanpa perilaku lain, Anda tidak akan tahu kapan selesai memuat. Jika penting untuk mengetahui kapan selesai memuat, Anda dapat mempertimbangkan untuk mengimplementasikan skrip di server Anda sendiri yang bertindak sebagai proxy.
VoteyDisciple
1

Faktanya, permintaan lintas-domain AJAX (XMLHttp) tidak diizinkan karena alasan keamanan (pikirkan tentang mengambil laman web "terbatas" dari sisi klien dan mengirimkannya kembali ke server - ini akan menjadi masalah keamanan).

Satu-satunya solusi adalah callback. Ini adalah: membuat objek skrip baru dan mengarahkan src ke JavaScript sisi-akhir, yang merupakan panggilan balik dengan nilai JSON (myFunction ({data}), myFunction adalah fungsi yang melakukan sesuatu dengan data (misalnya, menyimpannya dalam suatu variabel).

Adrián Navarro
sumber
1
benar, tapi saya bisa memuatnya dalam <script src = ""> atau <img src = ""> dan browser akan dengan senang hati menekannya. Saya hanya ingin tahu kapan itu dimuat penuh sehingga saya dapat meminta hasil impor.
Paul Tarjan
1

Cukup ubah "application / json" menjadi "text / plain" dan jangan lupa JSON.stringify (permintaan):

var request = {Company: sapws.dbName, UserName: username, Password: userpass};
    console.log(request);
    $.ajax({
        type: "POST",
        url: this.wsUrl + "/Login",
        contentType: "text/plain",
        data: JSON.stringify(request),

        crossDomain: true,
    });
David Lopes
sumber
1

Saya memiliki masalah yang sama. Perbaikan saya adalah menambahkan header ke skrip PHP saya yang hanya ada ketika di lingkungan dev.

Ini memungkinkan permintaan lintas domain:

header("Access-Control-Allow-Origin: *");

Ini memberi tahu permintaan preflight bahwa klien boleh mengirim header apa pun yang diinginkan:

header("Access-Control-Allow-Headers: *");

Dengan cara ini tidak perlu mengubah permintaan.

Jika Anda memiliki data sensitif dalam database dev Anda yang berpotensi bocor, maka Anda mungkin berpikir dua kali tentang ini.

fivebit
sumber
1

Dalam kasus saya, masalah ini tidak terkait dengan CORS karena saya mengeluarkan jQuery POST ke server web yang sama. Data itu JSON tapi saya telah menghilangkan dataType: parameter 'json'.

Saya tidak memiliki (juga saya tidak menambahkan) parameter contentType seperti yang ditunjukkan pada jawaban David Lopes di atas.

GarDavis
sumber
0

Itu tampak seperti Firefox dan Opera (diuji pada mac juga) tidak suka lintas domainness ini (tetapi Safari baik-baik saja dengan itu).

Anda mungkin harus memanggil kode sisi server lokal untuk meringkuk halaman jarak jauh.

helloandre
sumber
0

Saya dapat memperbaikinya dengan bantuan header berikut

Access-Control-Allow-Origin
Access-Control-Allow-Headers
Access-Control-Allow-Credentials
Access-Control-Allow-Methods

Jika Anda menggunakan Nodejs, ini adalah kode yang dapat Anda salin / tempel.

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin','*');
  res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  res.header('Access-Control-Allow-Credentials', true);
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH');
  next();
});
obai
sumber