ข้อผิดพลาด XmlHttpRequest: Origin null ไม่ได้รับอนุญาตจาก Access-Control-Allow-Origin


550

ฉันกำลังพัฒนาหน้าที่ดึงภาพจาก Flickr และ Panoramio ผ่านการสนับสนุน AJAX ของ jQuery

ด้าน Flickr ทำงานได้ดี แต่เมื่อฉันลอง$.get(url, callback)จาก Panoramio ฉันเห็นข้อผิดพลาดในคอนโซลของ Chrome:

XMLHttpRequest ไม่สามารถโหลดhttp://www.panoramio.com/wapi/data/get_photos?v=1&key=dummykey&tag=test&offset=0&length=20&callback=processImages&minx=-30&miny=0&maxx=0&maxy=150 Origin null ไม่ได้รับอนุญาตจาก Access-Control-Allow-Origin

หากฉันค้นหา URL นั้นจากเบราว์เซอร์โดยตรงจะทำงานได้ดี เกิดอะไรขึ้นและฉันจะแก้ไขสิ่งนี้ได้อย่างไร ฉันเขียนข้อความค้นหาไม่ถูกต้องหรือนี่คือสิ่งที่ Panoramio ทำเพื่อขัดขวางสิ่งที่ฉันพยายามทำ

Google ไม่ได้เปิดขึ้นตรงกับประโยชน์ใด ๆ เกี่ยวกับข้อผิดพลาด

แก้ไข

นี่คือตัวอย่างรหัสบางส่วนที่แสดงปัญหา:

$().ready(function () {
  var url = 'http://www.panoramio.com/wapi/data/get_photos?v=1&key=dummykey&tag=test&offset=0&length=20&callback=processImages&minx=-30&miny=0&maxx=0&maxy=150';

  $.get(url, function (jsonp) {
    var processImages = function (data) {
      alert('ok');
    };

    eval(jsonp);
  });
});

คุณสามารถรันตัวอย่างออนไลน์

แก้ไข 2

ขอบคุณดารินที่ช่วยเขาในเรื่องนี้ รหัสข้างต้นผิด ใช้สิ่งนี้แทน:

$().ready(function () {
  var url = 'http://www.panoramio.com/wapi/data/get_photos?v=1&key=dummykey&tag=test&offset=0&length=20&minx=-30&miny=0&maxx=0&maxy=150&callback=?';

  $.get(url, function (data) {
    // can use 'data' in here...
  });
});

1
อะไรดู URL เช่นที่คุณกำลังทำคำขอจาก ? สิ่งนี้ไม่ได้เกิดจากการสร้างขึ้นแบบไดนามิกiframeที่คุณdocument.writeเข้ามาเช่น?
Pekka

5
คุณสามารถโพสต์การตอบสนอง HTTP จากการร้องขอไปยังแต่ละบริการ ฉันพนันได้ว่า Panoramio ไม่ได้ให้บริการ Access-Control-Allow-Origin ดูw3.org/TR/corsสำหรับตัวอย่าง
Kevin Hakanson

2
@Kevin คุณไม่จำเป็นต้องมีส่วนหัวหากเซิร์ฟเวอร์ส่ง JSONP
ดารินทร์ดิมิทรอฟ

@Pekka ฉันกำลังเรียกใช้หน้าจากเครื่องท้องถิ่นของฉันในขณะนี้ ( file:///C:/) ไม่มีiframeส่วนเกี่ยวข้อง
Drew Noakes

1
@drew จะเกิดอะไรขึ้นหากคุณเรียกใช้จาก http URL ไม่ควรสร้างความแตกต่างด้วยวิธีนี้ แต่เพียงเพื่อแยกความเป็นไปได้
Pekka

คำตอบ:


423

สำหรับบันทึกเท่าที่ฉันสามารถบอกได้คุณมีสองปัญหา:

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

  2. ฉันเชื่อว่าคุณพูดถึงว่าคุณกำลังเรียกใช้จากไฟล์: // URL มีสองวิธีสำหรับส่วนหัว CORS เพื่อส่งสัญญาณว่า XHR ข้ามโดเมนตกลง หนึ่งคือการส่งAccess-Control-Allow-Origin: *(ซึ่งถ้าคุณเข้าถึง Flickr ผ่าน$.getพวกเขาจะต้องทำ) ในขณะที่อื่น ๆ คือการสะท้อนกลับเนื้อหาของOriginส่วนหัว อย่างไรก็ตามfile://URL จะสร้างค่า Null Originซึ่งไม่สามารถให้สิทธิ์ผ่าน echo-back ได้

$.getJSONครั้งแรกที่ได้รับการแก้ไขได้ในทางอ้อมโดยข้อเสนอแนะของดารินทร์กับการใช้งาน มันวิเศษเล็กน้อยที่จะเปลี่ยนประเภทคำขอจากค่าเริ่มต้นของ "json" เป็น "jsonp" หากเห็นสตริงย่อยcallback=?ใน URL

นั่นแก้ไขข้อที่สองโดยไม่พยายามทำการร้องขอ CORS จากfile://URL อีกต่อไป

ในการชี้แจงให้คนอื่นทราบต่อไปนี้เป็นคำแนะนำในการแก้ไขปัญหาอย่างง่าย:

  1. หากคุณกำลังพยายามใช้ JSONP ตรวจสอบให้แน่ใจว่าสิ่งใดสิ่งหนึ่งต่อไปนี้เป็นจริง:
    • คุณกำลังใช้$.getและการตั้งค่าไปdataTypejsonp
    • คุณกำลังใช้$.getJSONและรวมอยู่callback=?ใน URL
  2. หากคุณกำลังพยายามทำ XMLHttpRequest ข้ามโดเมนผ่านทาง CORS ...
    1. http://ให้แน่ใจว่าคุณกำลังทดสอบผ่าน สคริปต์ที่ทำงานผ่านทางfile://มีการสนับสนุนที่ จำกัด สำหรับ CORS
    2. ตรวจสอบให้แน่ใจว่าเบราว์เซอร์รองรับ CORSจริง (Opera และ Internet Explorer ช้าไปจนถึงงานปาร์ตี้)

45
แล้วทางออกสำหรับสิ่งนี้คืออะไร?
jQuerybeast

19
โทรกลับ =? FTW
Tim

10
เบราว์เซอร์บางตัวเช่นโครเมี่ยมอนุญาตให้ CORS หากเริ่มต้นด้วยพารามิเตอร์ --allow-file-access-from-files
echox

1
callback=?ไม่ทำงานสำหรับฉัน แต่jsonp=?ทำ มีคำอธิบายอะไรบ้าง?
crunkchitis

1
ฉันต้องติดตั้งเว็บเซิร์ฟเวอร์เพื่อทดสอบผ่านhttp://หรือไม่ หรือมีวิธีการเปิดไฟล์โดยใช้โปรโตคอลนั้นหรือไม่?
Will Sewell จะ

76

คุณอาจต้องเพิ่ม HEADER ในสคริปต์ที่เรียกว่านี่คือสิ่งที่ฉันต้องทำใน PHP:

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

รายละเอียดเพิ่มเติมในบริการข้ามโดเมน AJAX ou บริการเว็บ (ภาษาฝรั่งเศส)


1
@Uri: ขึ้นอยู่กับเซิร์ฟเวอร์ HTTP ของคุณ ด้วย Apache คุณจะต้องค้นหา mod_headers
ssokolow

4
@Uri <meta http-equiv = "การควบคุมการเข้าถึงอนุญาต - แหล่งกำเนิด" เนื้อหา = "*">
Herberth Amaral

7
@ HerberthAmaral ฉันพยายามเพิ่มสิ่งนี้ไว้ใน <head> </head> แต่มันไม่ได้ผลสำหรับฉัน ฉันลองใช้งานใน iOS Safari และ Chrome แต่ในคอนโซลฉันได้รับค่าเริ่มต้นเป็นโมฆะไม่ได้รับอนุญาตจากข้อผิดพลาด Access-Control-Allow-Origin
thandasoru

3
มันไม่ทำงานในส่วนหัวของไฟล์ html เพื่อเหตุผลด้านความปลอดภัย มันจะต้องมีส่วนหัว stackoverflow.com/questions/7015782/…
Justin Blank

ไม่สามารถอยู่ใน HTML ได้ มันจะต้องมีการระบุโดยโปรแกรมที่ส่งหน้า HTML ไปยังเบราว์เซอร์ของคุณผ่านเครือข่าย (เรียกว่าเว็บเซิร์ฟเวอร์)
Roman Plášil

70

สำหรับโครงการ HTML อย่างง่าย:

cd project
python -m SimpleHTTPServer 8000

จากนั้นเรียกดูไฟล์ของคุณ


1
ในขณะที่ยอดเยี่ยมและใช้งานได้ดีเมื่อคุณไปที่ 0.0.0.0:8000 และลองPOSTร้องขอที่คุณได้รับ: code 501, message Unsupported method ('POST')สำหรับ googles
pjammer

"เมื่อเว็บเซิร์ฟเวอร์ Python (เช่นเชอร์รี่จิเป็นต้น) บอกว่ากำลังให้บริการใน 0.0.0.0 หมายความว่ากำลังรับฟังปริมาณการใช้งาน TCP ทั้งหมดที่สิ้นสุดลงที่เครื่องนั้นไม่ว่าชื่อโฮสต์หรือ IP ที่ร้องขอจะเป็นอย่างไร" ดังนั้นอาจลองโพสต์ใน localhost: 8000 stackoverflow.com/a/4341808/102022
eric.christensen

20

ใช้งานได้กับฉันใน Google Chrome v5.0.375.127 (ฉันได้รับการแจ้งเตือน):

$.get('http://www.panoramio.com/wapi/data/get_photos?v=1&key=dummykey&tag=test&offset=0&length=20&callback=?&minx=-30&miny=0&maxx=0&maxy=150',
function(json) {
    alert(json.photos[1].photoUrl);
});

นอกจากนี้ฉันขอแนะนำให้คุณใช้$.getJSON()วิธีการแทนเพราะก่อนหน้านี้ไม่ทำงานบน IE8 (อย่างน้อยในเครื่องของฉัน):

$.getJSON('http://www.panoramio.com/wapi/data/get_photos?v=1&key=dummykey&tag=test&offset=0&length=20&callback=?&minx=-30&miny=0&maxx=0&maxy=150', 
function(json) {
    alert(json.photos[1].photoUrl);
});

คุณอาจจะลองออนไลน์จากที่นี่


UPDATE:

ตอนนี้คุณได้แสดงรหัสของคุณแล้วฉันสามารถดูปัญหาได้ คุณมีทั้งฟังก์ชั่นที่ไม่ระบุชื่อและฟังก์ชั่นแบบอินไลน์ processImagesแต่ทั้งสองจะถูกเรียกว่า นั่นคือการสนับสนุน JSONP ของ jQuery สังเกตว่าฉันกำหนดไว้อย่างไรcallback=?เพื่อที่คุณจะได้ใช้ฟังก์ชั่นนิรนาม คุณอาจจะอ่านรายละเอียดเพิ่มเติมเกี่ยวกับเรื่องนี้ในเอกสารประกอบ

ข้อสังเกตอีกอย่างคือคุณไม่ควรเรียก eval พารามิเตอร์ที่ส่งผ่านไปยังฟังก์ชันที่ไม่ระบุชื่อของคุณจะถูกวิเคราะห์คำใน JSON โดย jQuery


โอเคให้ฉันลองอีกครั้ง ฉันใช้ Chrome btw รุ่นอื่น "6.0.472.51 beta"
Drew Noakes

รหัสของคุณทำงานให้ฉัน ฉันได้อัปเดตคำถามของฉันด้วยรหัสบางอย่างที่ดึงปัญหาออกมา
Drew Noakes

ขอบคุณสำหรับเคล็ดลับใหม่ getJSON ... ฉันคดเคี้ยวตัวอย่าง jsFiddle ของคุณเพื่อแสดงปัญหา ( jsfiddle.net/ZfvKm ) และตอนนี้เห็นข้อความแสดงข้อผิดพลาดXMLHttpRequest ไม่สามารถโหลดpanoramio.com/wapi/data/... Origin fiddle.jshell.netไม่ได้รับอนุญาตจาก Access-Control-Allow-Origin
Drew Noakes

@Darin ขอขอบคุณสำหรับการอัปเดต ฉันเห็นประเด็นของคุณแล้วและฉันก็เล่นด้วยกันสองสามอย่าง แต่ฉันยังหาวิธีเข้าถึงวัตถุที่ส่งคืน คุณสามารถปรับปรุงตัวอย่าง jsFiddle ของคุณเพื่อแสดงการเข้าถึงข้อมูลได้หรือไม่? หากคุณตั้งค่าการโทร?กลับเป็น JSON ที่ส่งคืนจะถูกล้อมรอบด้วยวงเล็บ คุณไม่ได้กำหนดพารามิเตอร์สำหรับฟังก์ชั่นการโทรกลับของคุณและthisดูเหมือนจะไม่มีค่าของวัตถุตอบกลับ (อย่างน้อย.dataก็คือค่าnull)
Drew Noakes

@ ดาริน, มหัศจรรย์ ขอบคุณมาก. ทำงานได้ดีในขณะนี้ สำหรับคนอื่นอ่านนี้รวมของcallback=?บอก jQuery .getJSONเพื่อสร้างชื่อฟังก์ชันสุ่มภายในที่มีผลในการเรียกร้องให้ฟังก์ชั่นที่ไม่ระบุชื่อคุณส่งผ่านไป ชื่นชม
Drew Noakes

8

ตราบใดที่เซิร์ฟเวอร์ที่ร้องขอรองรับรูปแบบข้อมูล JSON ให้ใช้อินเตอร์เฟส JSONP (JSON Padding) ช่วยให้คุณสามารถร้องขอโดเมนภายนอกโดยไม่ต้องใช้พร็อกซีเซิร์ฟเวอร์หรือเนื้อหาส่วนหัวแฟนซี



4

เราจัดการผ่านhttp.confไฟล์ (แก้ไขแล้วเริ่มบริการ HTTP ใหม่):

<Directory "/home/the directory_where_your_serverside_pages_is">
    Header set Access-Control-Allow-Origin "*"
    AllowOverride all
    Order allow,deny
    Allow from all
</Directory>

ในHeader set Access-Control-Allow-Origin "*", คุณสามารถใส่ URL ที่แม่นยำ


1
สิ่งนี้ไม่สามารถใช้ได้กับ Apache ของ XAMPP ปัญหายังคงมีอยู่
Raptor

1
สิ่งนี้ไม่ปลอดภัย ดูที่นี่ทำไม; stackoverflow.com/questions/7564832/…
Rob

มันไม่ปลอดภัยอย่างที่ @RobQuist พูด
d337

4

หากคุณทำการทดสอบในพื้นที่หรือโทรหาไฟล์จากที่อื่นfile://คุณต้องปิดการใช้งานความปลอดภัยของเบราว์เซอร์

บน MAC: open -a Google\ Chrome --args --disable-web-security


3

ในกรณีของฉันรหัสเดียวกันทำงานได้ดีบน Firefox แต่ไม่ใช่ใน Google Chrome คอนโซล JavaScript ของ Google Chrome กล่าวว่า:

XMLHttpRequest cannot load http://www.xyz.com/getZipInfo.php?zip=11234. 
Origin http://xyz.com is not allowed by Access-Control-Allow-Origin.
Refused to get unsafe header "X-JSON"

ฉันต้องดร็อปส่วน www ของ Ajax URL เพื่อให้ตรงกับ URL ต้นทางอย่างถูกต้องและทำงานได้ดี


2

ไม่ใช่เซิร์ฟเวอร์ทั้งหมดที่สนับสนุน jsonp มันต้องการให้เซิร์ฟเวอร์ตั้งค่าฟังก์ชันการเรียกกลับในผลลัพธ์ ฉันใช้สิ่งนี้เพื่อรับการตอบกลับของ json จากไซต์ที่คืนค่า json บริสุทธิ์ แต่ไม่สนับสนุน jsonp:

function AjaxFeed(){

    return $.ajax({
        url:            'http://somesite.com/somejsonfile.php',
        data:           {something: true},
        dataType:       'jsonp',

        /* Very important */
        contentType:    'application/json',
    });
}

function GetData() {
    AjaxFeed()

    /* Everything worked okay. Hooray */
    .done(function(data){
        return data;
    })

    /* Okay jQuery is stupid manually fix things */
    .fail(function(jqXHR) {

        /* Build HTML and update */
        var data = jQuery.parseJSON(jqXHR.responseText);

        return data;
    });
}

1

ฉันใช้เซิร์ฟเวอร์ Apache ดังนั้นฉันจึงใช้โมดูล mod_proxy เปิดใช้งานโมดูล:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so

จากนั้นเพิ่ม:

ProxyPass /your-proxy-url/ http://service-url:serviceport/

สุดท้ายส่ง proxy-url ไปที่สคริปต์ของคุณ


1

ในฐานะที่เป็นโน้ตสุดท้ายเอกสาร Mozilla บอกอย่างชัดเจนว่า

ตัวอย่างด้านบนจะล้มเหลวหากส่วนหัวเป็น wildcarded เป็น: Access-Control-Allow-Origin: * เนื่องจาก Access-Control-Allow-Origin ระบุอย่างชัดเจนถึงhttp: //foo.example เนื้อหาของหนังสือรับรองจะถูกส่งกลับไปยังเนื้อหาเว็บที่เรียกใช้

ดังนั้นการใช้ '*' จึงไม่ใช่วิธีปฏิบัติที่ไม่ดี เพียงแค่ไม่ทำงาน :)


1

สำหรับ PHP - ใช้งานได้กับ Chrome, Safari และ firefox

https://w3c.github.io/webappsec-cors-for-developers/#avoid-returning-access-control-allow-origin-null

header('Access-Control-Allow-Origin: null');

การใช้ axios โทรบริการ php สดกับไฟล์: //


นอกจากนี้ยังใช้งานได้กับฉันสังเกตว่า null เป็น STRING และไม่ใช่ NULL จริง ฉันใช้เป็นheader('Access-Control-Allow-Origin: '.( trim($_SERVER['HTTP_REFERER'],'/') ?:'null'),true);ที่อนุญาตให้มีจุดกำเนิดข้ามจากเซิร์ฟเวอร์ระยะไกลไปยังอีกด้วยและตกไปที่ (สตริง) null สำหรับไฟล์ในเครื่อง
Louis Loudog Trottier

0

ฉันได้รับข้อผิดพลาดเดียวกันใน Chrome (ฉันไม่ได้ทดสอบเบราว์เซอร์อื่น) มันเป็นเพราะความจริงที่ว่าฉันกำลังนำทางบน domain.com แทนที่จะเป็น www.domain.com ค่อนข้างแปลก แต่ฉันสามารถแก้ปัญหาได้โดยเพิ่มบรรทัดต่อไปนี้ใน. htaccess มันเปลี่ยนเส้นทาง domain.com ไปที่ www.domain.com และปัญหาได้รับการแก้ไขแล้ว ฉันเป็นผู้เยี่ยมชมเว็บไซต์ขี้เกียจดังนั้นฉันแทบไม่เคยพิมพ์ www แต่เห็นได้ชัดว่าในบางกรณีจำเป็นต้องมี

RewriteEngine on
RewriteCond %{HTTP_HOST} ^domain\.com$ [NC]
RewriteRule ^(.*)$ http://www.domain.com/$1 [R=301,L]

0

ตรวจสอบให้แน่ใจว่าคุณใช้ JQuery เวอร์ชันล่าสุด เราพบข้อผิดพลาดนี้สำหรับ JQuery 1.10.2 และข้อผิดพลาดได้รับการแก้ไขหลังจากใช้ JQuery 1.11.1


0

folks,

ฉันพบปัญหาที่คล้ายกัน แต่ด้วยการใช้ Fiddler ฉันสามารถแก้ไขปัญหานี้ได้ ปัญหาคือว่าไคลเอนต์ URL ที่มีการกำหนดค่าในการใช้งาน CORS บนฝั่ง Web API ต้องไม่มีเครื่องหมายทับหน้าต่อท้าย หลังจากส่งคำขอของคุณผ่าน Google Chrome และตรวจสอบแท็บTextViewของส่วน Headersของ Fiddler แล้วข้อความแสดงข้อผิดพลาดจะแจ้งดังนี้:

* "นโยบายที่ระบุกำเนิด your_client_url: / 'ไม่ถูกต้องไม่สามารถจบด้วยเครื่องหมายทับหน้า"

นี่เป็นเรื่องแปลกจริงเพราะมันทำงานได้โดยไม่มีปัญหาใด ๆ ใน Internet Explorer แต่ทำให้ฉันปวดหัวเมื่อทดสอบโดยใช้ Google Chrome

ฉันลบเครื่องหมายทับหน้าในโค้ด CORS และคอมไพล์เว็บ API ใหม่และตอนนี้ API สามารถเข้าถึงได้ผ่าน Chrome และ Internet Explorer โดยไม่มีปัญหาใด ๆ กรุณาให้ภาพนี้

ขอบคุณแอนดี้


0

มีปัญหาเล็กน้อยในการแก้ปัญหาที่โพสต์โดย CodeGroover ด้านบนซึ่งหากคุณเปลี่ยนไฟล์คุณจะต้องรีสตาร์ทเซิร์ฟเวอร์เพื่อใช้ไฟล์ที่อัปเดตจริง ๆ (อย่างน้อยในกรณีของฉัน)

ดังนั้นค้นหาเล็กน้อยฉันพบสิ่งนี้ใช้:

sudo npm -g install simple-http-server # to install
nserver # to use

http://localhost:8000และจากนั้นก็จะทำหน้าที่ที่


1
แม้ว่าลิงก์นี้อาจตอบคำถามได้ดีกว่าหากรวมส่วนสำคัญของคำตอบไว้ที่นี่และให้ลิงก์สำหรับการอ้างอิง คำตอบสำหรับลิงก์เท่านั้นอาจไม่ถูกต้องหากหน้าเว็บที่เชื่อมโยงมีการเปลี่ยนแปลง - จากรีวิว
Vi100

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