jQuery XML 错误“请求的资源上不存在“Access-Control-Allow-Origin”标头。

IT技术 javascript jquery ajax xml-parsing cors
2021-01-23 12:26:36

我正在研究我的这个个人项目只是为了好玩,我想阅读位于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个回答

由于同源策略,您将无法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 调用


例子:

页面 athttp://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没有花哨标题的常规请求如果请求不简单(比如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.comwww.example.com:8080浏览器视为不同的域!是的,如果端口不同,那么就是不同的域!

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

最后的手段:如果您没有对服务的服务器端访问权限,您也可以镜像它(通过诸如反向代理之类的工具),并在那里包含所有必要的标头。

嗨,acdcjunior,我如何镜像我想要访问的 Web 服务?
2021-03-13 12:26:36
@Franva 您必须设置一个 HTTP 服务器(例如 Tomcat、带有 PHP 的 Apache、带有 ASP 的 IIS)并在那里放置一个页面,对于每个请求,它都会为实际服务(您正在镜像的服务)打开一个套接字,请求实际数据,然后将其作为响应。当然,您将通过代码(Java、PHP、ASP 等)来实现。
2021-03-19 12:26:36
@machinarium我不知道如果我理解你的意思,但我会尽力回答(告诉我,如果我有什么错):如果你输入在浏览器的地址栏中的URL,存在或不存在的Access-Control-Allow-Origin该URL的标题根本无关紧要 - 浏览器会像往常一样打开 URL。同源策略(以及对Access-Control-Allow-Origin标头的要求)仅适用于 Ajax 调用。
2021-03-19 12:26:36
谢谢,这是很多信息。现在我可以进行必要的研究以继续进行。
2021-03-23 12:26:36
@acdcjunior 如果我的理解正确,请纠正我。如果我直接在浏览器中输入一些 URL,它会自动重定向到新的域 url,而无需Access-Control-Allow-Origin例如,在使用 WIF 时,用户第一次登录时会被重定向到第三方登录页面。
2021-03-30 12:26:36

如果您的服务器上启用了 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");

?>

Php 似乎不介意该 url 是否来自不同的来源。就像我说的那样,这是一个笨拙的答案,我确定它有问题,但它对我有用。

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

?>

缓存代码取自这里

更好的解决方案是在服务器端缓存 XML 文件,并且仅file_get_contents在最新的 XML 文件具有足够的日期时才执行调用。另外,不要忘记您的 Content-Type 标头 :-)
2021-03-17 12:26:36
偶然发现了这个答案。问题:PHP 文件如何知道获取 GET 数据并将其提交到所述 URL?这也适用于 POSTed 数据吗?
2021-03-23 12:26:36
它不提交它。它获取文件并将其保存在本地,然后将其输出,就好像该文件是您在本地请求的 php 文件一样。如果本地缓存的文件早于给定的最大年龄,它将检索并保存它的另一个副本。
2021-04-04 12:26:36