89

我正在研究我的这个个人项目只是为了好玩,我想读取位于http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml的 xml 文件 并解析 xml 和使用它来转换货币之间的值。

到目前为止,我已经想出了下面的代码,这是非常基本的,以便读取 XML,但我收到以下错误。

XMLHttpRequest 无法加载 ****。请求的资源上不存在“Access-Control-Allow-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');
            }
         });
    }
);

我看不出我的代码有什么问题,所以我希望有人能指出我的代码做错了什么以及如何修复它。

4

2 回答 2

163

由于同源策略,您将无法http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml从部署的文件进行 ajax 调用。http://run.jsbin.com


由于源(又名origin)页面和目标URL 位于不同的域(run.jsbin.comwww.ecb.europa.eu),您的代码实际上是在尝试发出跨域(CORS)请求,而不是普通的GET.

简而言之,同源策略就是说浏览器应该只允许对 HTML 页面的同一域中的服务进行 ajax 调用。


例子:

at 的页面http://www.example.com/myPage.html只能直接请求 at 的服务http://www.example.com,例如http://www.example.com/api/myService. 如果服务托管在另一个域(例如http://www.ok.com/api/myService),浏览器将不会直接调用(如您所料)。相反,它将尝试发出 CORS 请求。

简而言之,要跨不同域执行 (CORS) 请求*,您的浏览器:

  • 将在原始请求中包含一个Origin标头(以页面的域为值)并照常执行;接着
  • 仅当服务器对该请求的响应包含允许 CORS 请求的足够标头(Access-Control-Allow-Origin其中之一时,浏览才会完成调用(几乎**与 HTML 页面位于同一域中的方式完全相同)。
    • 如果没有出现预期的标题,浏览器就会放弃(就像它对你所做的那样)。


* 上面描述了一个简单请求中的步骤,例如GET没有花哨的标头的常规。如果请求不简单(如POSTwith application/jsonas 内容类型),浏览器将暂停它片刻,并在完成之前先向OPTIONS目标 URL 发送请求。OPTIONS像上面一样,只有在对该请求的响应包含 CORS 标头时它才会继续。此OPTIONS调用称为预检请求。
** 我这么说几乎是因为常规调用和 CORS 调用之间存在其他差异。一个重要的一点是,即使响应中存在某些标头,如果它们不包含在Access-Control-Expose-Headers标头中,浏览器也不会选择它们。


如何解决?

这只是一个错字吗?有时 JavaScript 代码在目标域中只是一个错字。你检查过吗?如果页面在www.example.com它只会定期调用www.example.com!其他 URL,例如api.example.com甚至example.com或被浏览器www.example.com:8080视为不同的域!是的,如果端口不同,那就是不同的域!

添加标题。启用CORS的最简单方法是将必要的标头 (as Access-Control-Allow-Origin) 添加到服务器的响应中。(每种服务器/语言都有办法做到这一点 -在此处查看一些解决方案。)

最后的手段:如果您没有服务端访问服务,您也可以镜像它(通过反向代理等工具),并在其中包含所有必要的标头。

于 2013-11-06T20:12:54.590 回答
29

如果您在服务器上启用了 php,那么有一种 hack-tastic 方法可以做到这一点。更改此行:

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");

?>

如果该 url 来自不同的来源,Php 似乎并不介意。就像我说的,这是一个 hacky 答案,我确信它有问题,但它对我有用。

编辑:如果你想在 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;

?>

缓存代码取自这里

于 2013-12-17T22:11:00.553 回答