jQuery xml错误'请求的资源上没有'Access-Control-Allow-Origin'标头。


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');
            }
         });
    }
);

我没有发现我的代码有什么问题,所以我希望有人可以指出我的代码有什么问题以及如何解决。


2
我建议您阅读相同来源政策CORS
jmoerdyk,2013年

该错误逐字逐句地指出了错误所在。您的代码很好,问题出在您正在访问的服务器上。
凯文B

还可以看到MDN上的CORS
Amir Ali Akbari

Answers:


163

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


由于源页面(即原始页面)和目标 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在原始请求中包含标头(以页面的域为值),并照常执行;然后
  • 仅当服务器对该请求的响应包含允许CORS请求的足够的标头(Access-Control-Allow-Origin其中之一)时,浏览器才会完成调用(几乎**完全相同,如果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在服务器的响应中添加必要的标头(如)。(每种服务器/语言都可以做到这一点- 在此处检查一些解决方案。)

不得已:如果您没有服务器端对该服务的访问权限,则也可以对其进行镜像(通过反向代理之类的工具),并在其中包括所有必需的标头。


2
谢谢,这是很多信息。现在,我可以进行必要的研究以继续进行。
Bazinga777

1
您好acdcjunior,我该如何镜像要访问的Web服务?
弗朗瓦2014年

2
@Franva您必须设置一个HTTP服务器(例如Tomcat,带有PHP的Apache,带有ASP的IIS),并在其中放置一个页面,对于每个请求,该页面都会打开实际服务(您正在镜像的服务)的套接字,请求实际数据,然后将其作为响应。当然,您将通过代码(Java,PHP,ASP等)来实现。
acdcjunior 2014年

@acdcjunior如果我的理解正确,请纠正我。如果我直接在浏览器中输入一些URL,它将自动重定向到新的域URL,而无需Access-Control-Allow-Origin。例如,使用WIF时,首次登录时,用户将被重定向到第三方登录页面。
machinarium 2014年

@machinarium我不知道如果我理解你的意思,但我会尽力回答(告诉我,如果我有什么错):如果你输入在浏览器的地址栏中的URL,存在或不存在的Access-Control-Allow-Origin该URL的标头完全无关紧要-浏览器将照常打开URL。同源策略(和Access-Control-Allow-Origin标头要求)仅适用于Ajax调用。
acdcjunior 2014年

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似乎不介意该网址是否来自其他来源。就像我说的那样,这是一个错误的答案,我敢肯定这有问题,但是对我有用。

编辑:如果要在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文件有足够的日期时才执行调用。另外,别忘了您的Content-Type标头:-)
sffc

偶然发现了这个答案。问题:PHP文件如何知道获取GET数据并将其提交到所述URL?这也适用于POSTed数据吗?
mpdc

它不提交。它获取文件并将其保存在本地,然后将其喷出,就好像该文件是您在本地请求的php文件一样。如果本地缓存的文件早于给定的最长期限,它将检索并保存它的另一个副本。
以为
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.