为什么有些跨域JSON请求会失败,但有些则不然?

我在理解围绕JSON的安全性方面遇到了一些麻烦,因为理论上通常不应该工作的东西,似乎就是这样。 AFAIK,来自驻留在域A上的页面上的脚本的调用,不应该能够从域B接收数据。但是在下面的代码中,对一个外部域的调用失败,而另一个外部域的调用失败。 并且都没有打包JSON调用(jsonp)。

为什么是这样? 不应该不允许两者都通过浏览器安全检查吗? 我在Chrome和Firefox中获得了相同的结果。 如果我在dropbox.com上托管以下html页面,则Chrome会向我显示以下错误消息:

XMLHttpRequest无法加载http://www.odinfond.no/rest/fund/calc/fundReturn?&id=300&oneTimeInvestment=100000&oneTimeInvestmentDate=2009-11-01&endDate=2010-11-01&currency=NOK 。 Access-Control-Allow-Origin不允许来源http://dl.dropbox.com 。

通过单击此直接链接可以看到通过该呼叫我将获得的JSON响应。 对其他服务的调用成功返回。 我在dropbox上托管以下代码。 在这里尝试一下。

    JSON/JSONP test     service = 'http://www.odinfond.no/rest/fund/calc/fundReturn?'; parameters = { id: '300', oneTimeInvestment:'100000', oneTimeInvestmentDate:'2009-11-01', endDate:'2010-11-01', currency:'NOK' } $.getJSON( service, parameters, function(data) { alert("Success"); }); service = 'http://ws.geonames.org/postalCodeLookupJSON?' parameters = { postalcode:1540, country:'NO' } $.getJSON(service, parameters, function(data) { alert(data.postalcodes[0].adminName2); });  

Use Firebug to see JSON response

你会注意到工作请求有一个响应头:

 Access-Control-Allow-Origin: * 

这就是释放浏览器以使响应可用于脚本的原因。 (请注意, 始终发出请求,相同的源策略仅影响脚本是否可以访问响应)

如果’*’是主机名,则仅当当前文档的主机名与Access-Control-Allow-Origin标头匹配时才允许Access-Control-Allow-Origin

浏览源代码时 ,$ .ajax()似乎检测到远程URL并用旧的脚本标记替换AJAX(XMLHttpRequest):

  // Build temporary JSONP function if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) { jsonp = s.jsonpCallback || ("jsonp" + jsc++); // Replace the =? sequence both in the query string and the data if ( s.data ) { s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1"); } s.url = s.url.replace(jsre, "=" + jsonp + "$1"); // We need to make sure // that a JSONP style response is executed properly s.dataType = "script"; // Handle JSONP-style loading var customJsonp = window[ jsonp ]; window[ jsonp ] = function( tmp ) { if ( jQuery.isFunction( customJsonp ) ) { customJsonp( tmp ); } else { // Garbage collect window[ jsonp ] = undefined; try { delete window[ jsonp ]; } catch( jsonpError ) {} } data = tmp; jQuery.handleSuccess( s, xhr, status, data ); jQuery.handleComplete( s, xhr, status, data ); if ( head ) { head.removeChild( script ); } }; } 

[…]

  // Matches an absolute URL, and saves the domain var parts = rurl.exec( s.url ), remote = parts && (parts[1] && parts[1].toLowerCase() !== location.protocol || parts[2].toLowerCase() !== location.host); // If we're requesting a remote document // and trying to load JSON or Script with a GET if ( s.dataType === "script" && type === "GET" && remote ) { var head = document.getElementsByTagName("head")[0] || document.documentElement; var script = document.createElement("script"); if ( s.scriptCharset ) { script.charset = s.scriptCharset; } script.src = s.url; // Handle Script loading if ( !jsonp ) { var done = false; // Attach handlers for all browsers script.onload = script.onreadystatechange = function() { if ( !done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete") ) { done = true; jQuery.handleSuccess( s, xhr, status, data ); jQuery.handleComplete( s, xhr, status, data ); // Handle memory leak in IE script.onload = script.onreadystatechange = null; if ( head && script.parentNode ) { head.removeChild( script ); } } }; } // Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709 and #4378). head.insertBefore( script, head.firstChild ); // We handle everything using the script element injection return undefined; }