本文深入解析JavaScript跨域问题的成因,即浏览器同源策略限制。详细介绍了两种主流解决方案:利用script标签不受同源策略限制的JSONP技术,以及通过服务端设置Access-Control-Allow-Origin头部的CORS机制。文章对比了两者在实现原理、代码示例及浏览器兼容性(如IE10以下支持情况)上的差异,为开发者提供实用的跨域处理指南。
跨域问题的成因与场景
跨域问题的产生源于浏览器的安全机制。
浏览器遵循同源策略,限制当前域名的JavaScript脚本只能读取与当前域名相同的窗口属性。
跨域问题通常出现在特定的网络请求场景中。
当网页通过JavaScript尝试获取其他网站的数据时,例如使用Ajax请求第三方网站的天气、快递接口数据,或在Hybrid App中进行数据交互,浏览器会拦截请求并抛出错误。此时,必须解决JavaScript的跨域访问限制。
浏览器控制台通常会显示类似“XMLHttpRequest cannot load [请求域名]. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '[当前页域名]' is therefore not allowed access.”的错误信息。
判断是否产生跨域问题,需检查URL的组成部分。
一个完整的网址由协议名、子域名、主域名和端口号组成。例如在https://github.com/中,https是协议名,www是子域名,github是主域名,默认端口为80。如果在页面中发起的请求URL,其协议名、子域名、主域名或端口号中任意一项与当前页面不同,就会触发跨域限制。需要注意的是,即使是在http://localhost:80/页面请求http://127.0.0.1:80/,由于主机名不同,也会被视为跨域。
解决跨域的主要方案
解决跨域问题主要有以下几种技术手段。
常见的解决方案包括:
1. 使用JSONP;2. 通过服务端代理转发请求;3. 在服务端响应头中设置Access-Control-Allow-Origin,指定允许访问的域名。
JSONP原理与实现
下面详细讲解JSONP的实现方式。
首先需要明确,JSON(JavaScript Object Notation)与JSONP(JSON with Padding)并非同一概念。
理解其工作原理是解决问题的关键。
JSONP解决跨域的核心原理在于:浏览器的script标签不受同源策略限制,允许通过src属性加载CDN服务器上的静态文件。利用这一特性,可以通过script标签从服务器获取数据。在发起请求时,添加一个名为callback的参数,其值为前端定义的回调函数名。
以下是前端的实现示例。
以jQuery 2.1.3版本的ajax方法为例进行说明。
前端代码示例如下:
$.ajax({
url:"",
dataType:"jsonp",
data:{
params:""
}
}).done(function(data){
//dosomething..
})
仅在前端配置jsonp是不够的,因为jsonp请求本质上是插入script标签,与普通HTTP请求不同,它期望返回一段可执行的JavaScript代码。如果服务端直接返回JSON字符串,浏览器会报错。因此,服务端必须对返回数据进行特殊处理。
服务端需要配合返回处理后的数据。
既然利用script标签获取数据,而script标签用于加载JS代码,那么如何传递具体数据呢?
服务端需要检测请求参数中是否包含callback属性。如果存在,响应头Content-Type应设置为application/javascript,并将获取的数据作为callback函数的参数进行执行。以下是JSONP返回数据的格式示例。
服务端返回的代码示例如下:
/**/ typeof jQuery21307270454438403249_1428044213638 === 'function' && jQuery21307270454438403249_1428044213638({"code":1,"msg":"success","data":{"test":"test"}});
这是Express 4.12.3框架中关于JSONP的实现逻辑片段。
服务端JSONP处理逻辑
// jsonp
if (typeof callback === 'string' && callback.length !== 0) {
this.charset = 'utf-8';
this.set('X-Content-Type-Options', 'nosniff');
this.set('Content-Type', 'text/javascript');
// restrict callback charset
callback = callback.replace(/[^\[\]\w$.]/g, '');
// replace chars not allowed in JavaScript that are in JSON
body = body
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
// the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
// the typeof check is just to reduce client error noise
body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');';
}
CORS机制与浏览器兼容性
另一种解决方案是服务端设置Access-Control-Allow-Origin响应头。
这种方式要求服务端在Response Header中设置Access-Control-Allow-Origin为允许请求当前域名数据的特定域名,通常可设置为*以允许所有域名。配置完成后,客户端无需使用JSONP即可直接获取数据。关于将Access-Control-Allow-Origin设置为*是否存在安全风险,知乎上有一篇相关讨论可供参考。
http://www.zhihu.com/question/22992229
不同浏览器对CORS的支持程度存在差异。
Access-Control-Allow-Origin是HTML5新增的标准,IE10及以下版本的浏览器不支持该特性。因此,如果产品需要兼容PC端的老旧浏览器,建议使用服务端代理或JSONP方案。
以上内容涵盖了JavaScript跨域问题的核心原因及主要解决方案,希望能为学习JavaScript的开发者提供帮助。