jQuery xml error 'No' Access-Control-Allow-Origin 'มีอยู่ในทรัพยากรที่ร้องขอ'


89

ฉันกำลังทำโปรเจ็กต์ส่วนตัวของฉันเพื่อความสนุกที่ฉันต้องการอ่านไฟล์ xml ซึ่งอยู่ที่http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml และแยกวิเคราะห์ xml และ ใช้เพื่อแปลงค่าระหว่างสกุลเงิน

จนถึงตอนนี้ฉันได้สร้างโค้ดด้านล่างซึ่งค่อนข้างธรรมดาเพื่ออ่าน xml แต่ฉันได้รับข้อผิดพลาดต่อไปนี้

XMLHttpRequest ไม่สามารถโหลด **** ไม่มีส่วนหัว "Access-Control-Allow-Origin" ในทรัพยากรที่ร้องขอ Origin ' http://run.jsbin.com ' จึงไม่อนุญาตให้เข้าถึง

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

ฉันไม่เห็นอะไรผิดปกติกับรหัสของฉันดังนั้นฉันหวังว่าจะมีคนชี้ให้เห็นว่าฉันทำอะไรผิดกับรหัสของฉันและฉันจะแก้ไขได้อย่างไร


2
ฉันขอแนะนำให้คุณอ่านนโยบายแหล่งกำเนิดเดียวกันและCORS
jmoerdyk

ข้อผิดพลาดระบุว่าอะไรผิดคำต่อคำ รหัสของคุณใช้ได้ปัญหาเกิดจากเซิร์ฟเวอร์ที่คุณกำลังเข้าถึง
Kevin B

คำตอบ:


163

คุณจะไม่สามารถโทรหา ajax http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xmlจากไฟล์ที่ใช้งานได้ในเวลาhttp://run.jsbin.comอันเนื่องมาจากนโยบายที่มาเดียวกัน


ในฐานะที่เป็นแหล่งที่มา (aka กำเนิด ) และหน้าเป้าหมาย URL ที่อยู่ในโดเมนที่แตกต่างกัน ( run.jsbin.comและwww.ecb.europa.eu) รหัสของคุณเป็นจริงความพยายามที่จะทำให้การข้ามโดเมน ( ธ )GETขอไม่ธรรมดา

ในคำไม่กี่คำนโยบายที่มาเดียวกันกล่าวว่าเบราว์เซอร์ควรอนุญาตให้ ajax โทรไปยังบริการที่โดเมนเดียวกันของหน้า HTML


ตัวอย่าง:

หน้าที่http://www.example.com/myPage.htmlสามารถโดยตรงขอรับบริการที่มีอย่างเช่นhttp://www.example.com http://www.example.com/api/myServiceหากบริการโฮสต์อยู่ที่โดเมนอื่น (พูดhttp://www.ok.com/api/myService) เบราว์เซอร์จะไม่โทรออกโดยตรง (ตามที่คุณคาดหวัง) แต่จะพยายามส่งคำขอ CORS แทน

ในการดำเนินการในไม่ช้าเพื่อดำเนินการตามคำขอ (CORS) * ในโดเมนต่างๆเบราว์เซอร์ของคุณ:

  • จะรวมOriginส่วนหัวในคำขอเดิม (โดยมีโดเมนของเพจเป็นค่า) และดำเนินการตามปกติ แล้ว
  • เฉพาะในกรณีที่การตอบสนองของเซิร์ฟเวอร์ต่อคำขอนั้นมีส่วนหัวที่เพียงพอ ( Access-Control-Allow-Originเป็นหนึ่งในนั้น ) ที่อนุญาตให้มีการร้องขอ CORS การเรียกดูจะดำเนินการเรียกให้เสร็จสมบูรณ์ (เกือบจะ ** เหมือนกับที่หน้า HTML อยู่ในโดเมนเดียวกัน)
    • หากส่วนหัวที่คาดไว้ไม่มาเบราว์เซอร์ก็ยอมแพ้ (เช่นเดียวกับคุณ)


* ด้านบนแสดงให้เห็นขั้นตอนในคำของ่ายๆเช่นปกติที่GETไม่มีส่วนหัวแฟนซี หากคำขอไม่ง่าย (เช่นประเภทเนื้อหาPOSTwith application/jsonas) เบราว์เซอร์จะพักไว้สักครู่และก่อนที่จะดำเนินการOPTIONSตามคำขอจะส่งคำขอไปยัง URL เป้าหมายก่อน เช่นเดียวกับข้างต้นจะดำเนินการต่อเมื่อการตอบสนองต่อOPTIONSคำขอนี้มีส่วนหัว CORS นี้OPTIONSโทรเป็นที่รู้จักกันpreflightคำขอ
** ฉันพูดว่าเกือบจะเป็นเพราะมีความแตกต่างอื่น ๆ ระหว่างการโทรปกติและการโทร CORS สิ่งที่สำคัญคือบางส่วนหัวแม้ว่าจะมีอยู่ในการตอบกลับ แต่เบราว์เซอร์Access-Control-Expose-Headersจะไม่หยิบขึ้นมาหากไม่รวมอยู่ในส่วนหัว


ต้องแก้ไขอย่างไร?

มันเป็นเพียงการพิมพ์ผิด? บางครั้งโค้ด JavaScript มีเพียงการพิมพ์ผิดในโดเมนเป้าหมาย คุณตรวจสอบแล้วหรือยัง? ถ้าหน้าwww.example.comมันจะโทรไปหาปกติเท่านั้นwww.example.com! URL อื่น ๆ เช่นapi.example.comหรือแม้กระทั่งexample.comหรือwww.example.com:8080ถือว่าเป็นโดเมนที่แตกต่างกันโดยเบราว์เซอร์! ใช่ถ้าพอร์ตแตกต่างกันแสดงว่าเป็นโดเมนอื่น!

เพิ่มส่วนหัว วิธีที่ง่ายที่สุดในการเปิดใช้งาน CORS คือการเพิ่มส่วนหัวที่จำเป็น (เป็นAccess-Control-Allow-Origin) ให้กับการตอบสนองของเซิร์ฟเวอร์ (แต่ละเซิร์ฟเวอร์ / ภาษามีวิธีการทำเช่นนั้น - ตรวจสอบวิธีแก้ปัญหาบางอย่างที่นี่ )

ทางเลือกสุดท้าย:หากคุณไม่มีสิทธิ์เข้าถึงบริการจากฝั่งเซิร์ฟเวอร์คุณยังสามารถมิเรอร์ได้ (ผ่านเครื่องมือเช่นพร็อกซีย้อนกลับ ) และรวมส่วนหัวที่จำเป็นทั้งหมดไว้ที่นั่น


2
ขอบคุณข้อมูลมากมาย ตอนนี้ฉันสามารถทำการวิจัยที่จำเป็นเพื่อดำเนินการต่อได้
Bazinga777

1
สวัสดี acdcjunior ฉันจะจำลองบริการเว็บที่ฉันต้องการเข้าถึงได้อย่างไร
Franva

2
@Franva คุณจะต้องตั้งค่าเซิร์ฟเวอร์ HTTP (เช่น Tomcat, Apache ด้วย PHP, IIS พร้อม ASP) และวางหน้าไว้ที่นั่นสำหรับทุกคำขอจะเปิดซ็อกเก็ตไปยังบริการจริง (บริการที่คุณกำลังมิเรอร์) ร้องขอข้อมูลจริงจากนั้นให้เป็นการตอบสนอง แน่นอนคุณจะทำเช่นนั้นโดยใช้รหัส (Java, PHP, ASP ฯลฯ )
acdcjunior

@acdcjunior โปรดแก้ไขฉันหากความเข้าใจของฉันถูกต้อง ถ้าฉันป้อน URL บางอย่างในเบราว์เซอร์โดยตรงก็จะเปลี่ยนเส้นทางไปยัง URL โดเมนใหม่โดยอัตโนมัติโดยไม่ต้องควบคุมการเข้าถึง-อนุญาตให้-แหล่งกำเนิดสินค้า ตัวอย่างเช่นเมื่อใช้ WIF ผู้ใช้จะถูกเปลี่ยนเส้นทางไปยังหน้าเข้าสู่ระบบของบุคคลที่สามเมื่อเข้าสู่ระบบเป็นครั้งแรก
machinarium

@machinarium ฉันไม่แน่ใจว่าฉันเข้าใจว่าคุณหมายถึงอะไร แต่ฉันจะพยายามตอบ (บอกฉันว่าฉันมีอะไรผิดพลาด): หากคุณป้อน URL ในแถบที่อยู่ของเบราว์เซอร์การมีหรือไม่มีAccess-Control-Allow-Originใน URL นั้น ส่วนหัวจะไม่สำคัญเลย - เบราว์เซอร์จะเปิด URL ตามปกติ นโยบายต้นกำเนิดเดียวกัน (และข้อกำหนดสำหรับAccess-Control-Allow-Originส่วนหัว) ใช้กับการโทร Ajax เท่านั้น
acdcjunior

29

มีวิธีการแฮ็กแบบหนึ่งหากคุณเปิดใช้งาน php บนเซิร์ฟเวอร์ของคุณ เปลี่ยนบรรทัดนี้:

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

ถึงบรรทัดนี้:

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

จากนั้นในสคริปต์ php (หากคุณได้รับอนุญาตให้ใช้ฟังก์ชัน file_get_contents ()):

<?php

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

?>

ดูเหมือนว่า PHP จะไม่สนใจหาก url นั้นมาจากแหล่งกำเนิดอื่น อย่างที่ฉันพูดนี่เป็นคำตอบที่แฮ็คและฉันแน่ใจว่ามีบางอย่างผิดปกติ แต่มันก็ใช้ได้กับฉัน

แก้ไข: หากคุณต้องการแคชผลลัพธ์ใน php นี่คือไฟล์ php ที่คุณจะใช้:

<?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;

?>

แคชรหัสเอาจากที่นี่


3
วิธีแก้ปัญหาที่ดีกว่านั้นคือการแคชไฟล์ XML ที่ฝั่งเซิร์ฟเวอร์และทำการfile_get_contentsโทรก็ต่อเมื่อไฟล์ XML ล่าสุดลงวันที่เพียงพอแล้ว นอกจากนี้อย่าลืมส่วนหัวประเภทเนื้อหาของคุณ :-)
sffc

สะดุดกับคำตอบนี้ คำถาม: ไฟล์ PHP รู้ได้อย่างไรว่าจะรับข้อมูล GET และส่งไปยัง URL ดังกล่าว? สิ่งนี้จะใช้ได้กับข้อมูล POSTed ด้วยหรือไม่
mpdc

มันไม่ยื่นให้ รับไฟล์และบันทึกไว้ในเครื่องจากนั้นพ่นออกมาราวกับว่าไฟล์นั้นเป็นไฟล์ php ที่คุณร้องขอในเครื่อง มันจะดึงและบันทึกสำเนาอื่นหากไฟล์ที่แคชในเครื่องเก่ากว่าอายุสูงสุดที่กำหนด
ความคิด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.