jQuery xml lỗi 'Không có tiêu đề' Access-Control-Allow-Origin 'có trên tài nguyên được yêu cầu.'


89

Tôi đang thực hiện dự án cá nhân này của mình chỉ để giải trí khi tôi muốn đọc một tệp xml có tại http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml và phân tích cú pháp xml và sử dụng nó để chuyển đổi giá trị giữa các loại tiền tệ.

Cho đến nay tôi đã nghĩ ra đoạn mã dưới đây khá cơ bản để đọc xml nhưng tôi gặp lỗi sau.

XMLHttpRequest không thể tải ****. Không có tiêu đề 'Access-Control-Allow-Origin' có trên tài nguyên được yêu cầu. Nguồn gốc ' http://run.jsbin.com ' do đó không được phép truy cập.

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

Tôi không thấy bất cứ điều gì sai với mã của mình vì vậy tôi hy vọng ai đó có thể chỉ ra những gì tôi đang làm sai với mã của mình và cách tôi có thể sửa nó.


2
Tôi khuyên bạn nên đọc Chính sách Nguồn gốc Giống nhauCORS
jmoerdyk

lỗi chỉ ra chính xác những gì sai, từng chữ. Mã của bạn ổn, vấn đề là ở máy chủ bạn đang truy cập.
Kevin B

Câu trả lời:


163

Bạn sẽ không thể thực hiện cuộc gọi ajax đến http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xmltừ tệp được triển khai tại http://run.jsbin.comdo chính sách nguồn gốc giống nhau .


Vì trang nguồn (hay còn gọi là trang gốc ) và URL mục tiêu nằm ở các tên miền ( run.jsbin.comwww.ecb.europa.eu) khác nhau , mã của bạn thực sự đang cố gắng thực hiện một yêu cầu Tên miền chéo (CORS) , không phải là một việc bình thường GET.

Nói cách khác, chính sách cùng nguồn gốc nói rằng các trình duyệt chỉ nên cho phép các lệnh gọi ajax đến các dịch vụ trên cùng một miền của trang HTML.


Thí dụ:

Một trang tại http://www.example.com/myPage.htmlchỉ có thể yêu cầu trực tiếp các dịch vụ tại http://www.example.com, như http://www.example.com/api/myService. Nếu dịch vụ được lưu trữ tại một miền khác (giả sử http://www.ok.com/api/myService), trình duyệt sẽ không thực hiện cuộc gọi trực tiếp (như bạn mong đợi). Thay vào đó, nó sẽ cố gắng thực hiện một yêu cầu CORS.

Nói ngắn gọn, để thực hiện yêu cầu (CORS) * trên các miền khác nhau, trình duyệt của bạn:

  • Sẽ bao gồm một Origintiêu đề trong yêu cầu ban đầu (với tên miền của trang là giá trị) và thực hiện nó như bình thường; và sau đó
  • Chỉ khi phản hồi của máy chủ cho yêu cầu đó chứa các tiêu đề phù hợp ( Access-Control-Allow-Originmột trong số chúng ) cho phép yêu cầu CORS, trình duyệt sẽ hoàn thành lệnh gọi (gần như ** chính xác như cách nó sẽ xảy ra nếu trang HTML ở cùng một miền).
    • Nếu các tiêu đề mong đợi không xuất hiện, trình duyệt sẽ bỏ cuộc (giống như nó đã làm với bạn).


* Phần trên mô tả các bước trong một yêu cầu đơn giản , chẳng hạn như một yêu cầu thông thường GETkhông có tiêu đề cầu kỳ. Nếu yêu cầu không đơn giản (như loại nội dung POSTvới application/jsonas), trình duyệt sẽ giữ nó trong giây lát và trước khi thực hiện, trước tiên sẽ gửi OPTIONSyêu cầu đến URL đích. Giống như ở trên, nó sẽ chỉ tiếp tục nếu phản hồi cho OPTIONSyêu cầu này có chứa tiêu đề CORS. Cuộc OPTIONSgọi này được gọi là yêu cầu trước khi bay .
** Tôi đang nói gần như vì có sự khác biệt khác giữa các cuộc gọi thông thường và các cuộc gọi CORS. Một điều quan trọng là một số tiêu đề, ngay cả khi có trong phản hồi, sẽ không được trình duyệt chọn nếu chúng không được đưa vàoAccess-Control-Expose-Headers tiêu đề.


Làm thế nào để sửa chữa nó?

Nó chỉ là một lỗi đánh máy? Đôi khi mã JavaScript chỉ có lỗi đánh máy trong miền đích. Bạn coi lại chưa? Nếu trang ở www.example.comđó, nó sẽ chỉ thực hiện các cuộc gọi thông thường đến www.example.com! Các URL khác, chẳng hạn như api.example.comhoặc thậm chí example.comhoặc www.example.com:8080được trình duyệt coi là các miền khác nhau ! Có, nếu cổng khác, thì đó là một miền khác!

Thêm tiêu đề. Cách đơn giản nhất để kích hoạt CORS là thêm các tiêu đề cần thiết (as Access-Control-Allow-Origin) vào các phản hồi của máy chủ. (Mỗi máy chủ / ngôn ngữ có một cách để làm điều đó - hãy kiểm tra một số giải pháp tại đây .)

Phương án cuối cùng: Nếu bạn không có quyền truy cập từ phía máy chủ vào dịch vụ, bạn cũng có thể nhân bản nó (thông qua các công cụ như proxy ngược ) và bao gồm tất cả các tiêu đề cần thiết ở đó.


2
Cảm ơn, đó là rất nhiều thông tin. Bây giờ tôi có thể thực hiện các nghiên cứu cần thiết để tiến hành.
Bazinga777

1
Xin chào acdcjunior, làm cách nào để phản chiếu dịch vụ web mà tôi muốn truy cập?
Franva

2
@Franva Bạn sẽ phải thiết lập một máy chủ HTTP (ví dụ: Tomcat, Apache với PHP, IIS với ASP) và đặt một trang ở đó, đối với mọi yêu cầu, nó sẽ mở một ổ cắm cho dịch vụ thực tế (dịch vụ bạn đang sao chép), yêu cầu dữ liệu thực tế và sau đó đưa ra phản hồi. Tất nhiên, bạn sẽ làm điều đó thông qua mã (Java, PHP, ASP, v.v.).
acdcjunior

@acdcjunior Vui lòng sửa cho tôi nếu sự hiểu biết của tôi là đúng. Nếu tôi nhập trực tiếp một số URL vào trình duyệt, nó sẽ tự động chuyển hướng đến url miền mới mà không cần Access-Control-Allow-Origin . Ví dụ: khi sử dụng WIF, người dùng sẽ được chuyển hướng đến trang đăng nhập của bên thứ ba khi đăng nhập lần đầu tiên.
Machinarium

@machinarium Tôi không chắc mình có hiểu ý bạn không, nhưng tôi sẽ cố gắng trả lời (cho tôi biết nếu tôi có sai sót gì không): Nếu bạn nhập URL vào thanh địa chỉ của trình duyệt, thì sự hiện diện hay vắng mặt của Access-Control-Allow-OriginURL đó tiêu đề sẽ không thành vấn đề - trình duyệt sẽ mở URL như bình thường. Chính sách cùng nguồn gốc (và yêu cầu đối với Access-Control-Allow-Origintiêu đề) chỉ áp dụng cho các lệnh gọi Ajax.
acdcjunior

29

Có một loại cách hack-tastic để làm điều đó nếu bạn đã bật php trên máy chủ của mình. Thay đổi dòng này:

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

đến dòng này:

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

và sau đó trong tập lệnh php (nếu bạn có quyền sử dụng hàm file_get_contents ()):

<?php

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

?>

Php dường như không bận tâm nếu url đó có nguồn gốc khác. Như tôi đã nói, đây là một câu trả lời khó hiểu và tôi chắc chắn rằng có điều gì đó không ổn với nó, nhưng nó phù hợp với tôi.

Chỉnh sửa: Nếu bạn muốn lưu kết quả vào bộ nhớ cache trong php, đây là tệp php bạn sẽ sử dụng:

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

?>

Mã bộ nhớ đệm lấy từ đây .


3
Một giải pháp tốt hơn nữa sẽ là lưu vào bộ đệm tệp XML ở phía máy chủ và chỉ thực hiện lệnh file_get_contentsgọi nếu tệp XML gần đây nhất đủ ngày tháng. Ngoài ra, đừng quên tiêu đề Content-Type của bạn :-)
sffc

Tình cờ khi có câu trả lời này. Câu hỏi: Làm cách nào để tệp PHP biết lấy dữ liệu GET và gửi nó đến URL đã nói? Điều này cũng sẽ hoạt động với dữ liệu POSTed?
mpdc

Nó không nộp nó. Nó lấy tệp và lưu cục bộ, sau đó phun ra như thể tệp đó là tệp php bạn đang yêu cầu cục bộ. Nó sẽ truy xuất và lưu một bản sao khác của nó nếu tệp được lưu trong bộ nhớ cache cục bộ cũ hơn tuổi tối đa đã cho.
Suy nghĩ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.