Kesalahan jQuery xml 'Tidak ada header' Access-Control-Allow-Origin 'yang ada pada sumber daya yang diminta.'

89

Saya mengerjakan proyek pribadi saya ini hanya untuk bersenang-senang di mana saya ingin membaca file xml yang terletak di http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml dan mengurai xml dan gunakan untuk mengonversi nilai antar mata uang.

Sejauh ini saya telah menemukan kode di bawah ini yang cukup mendasar untuk membaca xml tetapi saya mendapatkan kesalahan berikut.

XMLHttpRequest tidak dapat memuat ****. Tidak ada header 'Access-Control-Allow-Origin' yang ada di resource yang diminta. Oleh karena itu, asal ' http://run.jsbin.com ' tidak diizinkan untuk diakses.

$(document).ready( 
    function() {     
        $.ajax({          
            type:  'GET',
            url:   'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml',
            dataType: 'xml',              
            success: function(xml){
                alert('aaa');
            }
         });
    }
);

Saya tidak melihat ada yang salah dengan kode saya, jadi saya berharap seseorang dapat menunjukkan kesalahan yang saya lakukan dengan kode saya dan bagaimana saya bisa memperbaikinya.

Bazinga777
sumber
2
Saya sarankan Anda membaca tentang Kebijakan Asal yang Sama dan CORS
jmoerdyk
kesalahan menyatakan dengan tepat apa yang salah, kata demi kata. Kode Anda baik-baik saja, masalahnya ada pada server yang Anda akses.
Kevin B
dan juga lihat CORS di MDN
Amir Ali Akbari

Jawaban:

163

Anda tidak akan dapat melakukan panggilan ajax ke http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xmldari file yang diterapkan di http://run.jsbin.comkarena kebijakan asal yang sama .


Karena halaman sumber (alias asal ) dan URL target berada di domain yang berbeda ( run.jsbin.comdan www.ecb.europa.eu), kode Anda sebenarnya mencoba membuat permintaan Cross-domain (CORS) , bukan hal biasa GET.

Singkatnya, kebijakan yang sama-asal mengatakan bahwa browser hanya mengizinkan panggilan ajax ke layanan di domain yang sama pada halaman HTML.


Contoh:

Halaman di http://www.example.com/myPage.htmlhanya dapat secara langsung meminta layanan yang ada di http://www.example.com, seperti http://www.example.com/api/myService. Jika layanan dihosting di domain lain (misalnya http://www.ok.com/api/myService), browser tidak akan melakukan panggilan secara langsung (seperti yang Anda harapkan). Sebaliknya, ia akan mencoba membuat permintaan CORS.

Singkatnya, untuk melakukan permintaan (CORS) * di berbagai domain, browser Anda:

  • Akan menyertakan Originheader dalam permintaan asli (dengan domain halaman sebagai nilai) dan melakukannya seperti biasa; lalu
  • Hanya jika server respon permintaan yang berisi header yang memadai ( Access-Control-Allow-Originadalah salah satu dari mereka ) yang memungkinkan CORS meminta, browse akan menyelesaikan panggilan (hampir ** persis dengan cara yang akan jika halaman HTML berada di domain yang sama).
    • Jika header yang diharapkan tidak muncul, browser akan menyerah begitu saja (seperti yang terjadi pada Anda).


* Di atas menggambarkan langkah-langkah dalam permintaan sederhana , seperti permintaan biasa GETtanpa header mewah. Jika permintaan tidak sederhana (seperti POSTdengan application/jsonsebagai tipe konten), browser akan menahannya sebentar, dan, sebelum memenuhinya, terlebih dahulu akan mengirim OPTIONSpermintaan ke URL target. Seperti di atas, ini hanya akan berlanjut jika tanggapan atas OPTIONSpermintaan ini berisi header CORS. Ini OPTIONSpanggilan dikenal sebagai preflight permintaan.
** Saya katakan hampir karena ada perbedaan lain antara panggilan biasa dan panggilan CORS. Yang penting adalah bahwa beberapa header, meskipun ada dalam respons, tidak akan diambil oleh browser jika tidak disertakan dalamAccess-Control-Expose-Headers header.


Bagaimana memperbaikinya?

Apakah itu hanya salah ketik? Terkadang kode JavaScript hanya memiliki kesalahan ketik di domain target. Sudahkah kamu periksa? Jika halaman di www.example.comitu hanya akan membuat panggilan biasa ke www.example.com! URL lain, seperti api.example.comatau bahkan example.comatau www.example.com:8080dianggap domain berbeda oleh browser! Ya, jika porta berbeda, maka itu adalah domain yang berbeda!

Tambahkan header. Cara termudah untuk mengaktifkan CORS adalah dengan menambahkan header (sebagai Access-Control-Allow-Origin) yang diperlukan ke respons server. (Setiap server / bahasa memiliki cara untuk melakukan itu - periksa beberapa solusi di sini .)

Pilihan terakhir: Jika Anda tidak memiliki akses sisi server ke layanan, Anda juga dapat mencerminkannya (melalui alat seperti proxy terbalik ), dan menyertakan semua tajuk yang diperlukan di sana.

acdcjunior.dll
sumber
2
Terima kasih banyak informasi. Sekarang saya dapat melakukan penelitian yang diperlukan untuk melanjutkan.
Bazinga777
1
Hai acdcjunior, bagaimana cara mencerminkan layanan web yang ingin saya akses?
Franva
2
@Franva Anda harus menyiapkan server HTTP (mis. Tomcat, Apache dengan PHP, IIS dengan ASP) dan menempatkan halaman di sana yang, untuk setiap permintaan, membuka soket ke layanan sebenarnya (layanan yang Anda mirroring), meminta data aktual dan kemudian memberikannya sebagai tanggapan. Tentu saja, Anda akan melakukannya melalui kode (Java, PHP, ASP, dll.).
acdcjunior
@acdcjunior Harap perbaiki saya jika pemahaman saya benar. Jika saya memasukkan beberapa URL di browser secara langsung, itu akan mengarahkan ke url domain baru secara otomatis tanpa Access-Control-Allow-Origin . Misalnya, saat menggunakan WIF, pengguna akan diarahkan ke halaman login pihak ketiga saat login untuk pertama kali.
machinarium
@machinarium Saya tidak yakin apakah saya mengerti maksud Anda, tetapi saya akan mencoba menjawab (beri tahu saya jika ada yang salah): Jika Anda memasukkan URL di bilah alamat browser, ada atau tidaknya Access-Control-Allow-OriginURL itu header sama sekali tidak penting - browser akan membuka URL seperti biasa. Kebijakan yang sama-asal (dan persyaratan untuk Access-Control-Allow-Originheader) hanya berlaku untuk panggilan Ajax.
acdcjunior
29

Ada semacam cara hack-tastic untuk melakukannya jika Anda mengaktifkan php di server Anda. Ubah baris ini:

url:   'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml',

ke baris ini:

url: '/path/to/phpscript.php',

dan kemudian di skrip php (jika Anda memiliki izin untuk menggunakan fungsi file_get_contents ()):

<?php

header('Content-type: application/xml');
echo file_get_contents("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml");

?>

Php sepertinya tidak keberatan jika url itu berasal dari sumber yang berbeda. Seperti yang saya katakan, ini adalah jawaban yang meretas, dan saya yakin ada yang salah dengan itu, tetapi berhasil untuk saya.

Edit: Jika Anda ingin meng-cache hasilnya di php, berikut file php yang akan Anda gunakan:

<?php

$cacheName = 'somefile.xml.cache';
// generate the cache version if it doesn't exist or it's too old!
$ageInSeconds = 3600; // one hour
if(!file_exists($cacheName) || filemtime($cacheName) > time() + $ageInSeconds) {
  $contents = file_get_contents('http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml');
  file_put_contents($cacheName, $contents);
}

$xml = simplexml_load_file($cacheName);

header('Content-type: application/xml');
echo $xml;

?>

Kode cache diambil dari sini .

jyapayne
sumber
3
Solusi yang lebih baik adalah dengan menyimpan file XML di sisi server dan hanya melakukan file_get_contentspanggilan jika file XML terbaru memiliki tanggal yang memadai. Juga, jangan lupa header Jenis Konten Anda :-)
sffc
Tersandung pada jawaban ini. Pertanyaan: Bagaimana file PHP mengetahui cara mengambil data GET dan mengirimkannya ke URL tersebut? Apakah ini akan berfungsi dengan data yang diposkan juga?
mpdc
Itu tidak mengirimkannya. Itu mendapatkan file dan menyimpannya secara lokal, lalu memuntahkannya seolah-olah file itu adalah file php yang Anda minta secara lokal. Ini akan mengambil dan menyimpan salinan lain jika file yang di-cache secara lokal lebih tua dari usia maksimal yang diberikan.
Dipikir