jQuery xml错误'No'Access-Control-Allow-Origin'标头出现在请求的资源上.

Baz*_*777 89 javascript ajax jquery xml-parsing cors

我正在研究我的这个个人项目,只是为了好玩,我想阅读一个xml文件,该文件位于http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.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');
            }
         });
    }
);
Run Code Online (Sandbox Code Playgroud)

我没有看到我的代码有任何问题所以我希望有人可以指出我的代码错误,以及我如何解决它.

acd*_*ior 163

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


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

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


例:

一个页面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没有花哨标题的常规请求.如果请求不简单(例如POST带有application/json内容类型),浏览器会暂时保留它,并且在完成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 的最简单方法是Access-Control-Allow-Origin在服务器的响应中添加必要的标头(as ).(每种服务器/语言都有办法做到这一点 - 在这里查看一些解决方案.)

最后一种方法:如果您没有服务器端的服务器访问权限,您也可以镜像它(通过反向代理等工具),并在那里包含所有必需的标头.

  • 谢谢,这是很多信息.现在我可以进行必要的研究. (2认同)
  • @Franva你必须设置一个HTTP服务器(例如Tomcat,带PHP的Apache,带ASP的IIS)并在那里放置一个页面,对于每个请求,它打开一个套接到实际服务(你正在镜像的服务),请求实际数据,然后将其作为响应.当然,你可以通过代码(Java,PHP,ASP等)来实现. (2认同)

jya*_*yne 29

如果您在服务器上启用了php,那么有一种非常好的方法可以做到这一点.改变这一行:

url:   'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml',
Run Code Online (Sandbox Code Playgroud)

到这一行:

url: '/path/to/phpscript.php',
Run Code Online (Sandbox Code Playgroud)

然后在php脚本中(如果您有权使用file_get_contents()函数):

<?php

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

?>
Run Code Online (Sandbox Code Playgroud)

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;

?>
Run Code Online (Sandbox Code Playgroud)

缓存代码取自此处.

  • 更好的解决方案是在服务器端缓存XML文件,并且只有在最新的XML文件足够过时才执行`file_get_contents`调用.另外,不要忘记您的Content-Type标题:-) (3认同)