引言
使用XHR对象进行Ajax通讯的主要限制,就是跨域的资源访问。默认情况下,XHR只能访问与包含它的页面位于同一个域中的资源。这种安全策略可以预防某些恶意的行为。但是,实现合理的跨与请求对于开发某些应用程序也是必要的。下面,我们就来介绍下关于跨域这方面的知识。
CORS简介
CORS(跨域资源共享)是W3C定义的一个工作草案,定义了在必须进行跨域资源访问时,浏览器与服务器之间的通讯。CORS背后的思想是这样的:使用自定义的Http头部让浏览器和服务器进行通讯,从而决定响应是成功的还是失败的。
例如我们使用GET或者POST发送一个请求时,它默认是没有自定义头部的。在发送该请求时,需要额外添加一个Origin头部(包含请求页面的源信息:协议、域名和端口),以便服务器根据这个头部来决定是否给予响应。
例如:我们需要跨域访问http://www.nczonline.net。那么浏览器会将Origin设置为这个URL地址。如果服务器认为这个请求可以接受,就在Access-Control-Allow-Origin中回发相同的源信息。如果没有这个头部,或者这个头部源信息不匹配,浏览器就会拒绝进行跨域的请求。
下面介绍一个完整的例子,如下:
假设我们页面或者应用已在 http://www.test1.com 上了,而我们打算从 http://www.test2.com 请求提取数据。一般情况下,如果我们直接使用 AJAX 来请求将会失败,浏览器也会返回“源不匹配”的错误,这就是跨域。
IE对CORS的实现
IE8中引入了XDR(XDomainRequest)对象来支持跨域的请求。这个对象与XHR对象类似,但它可以实现安全可靠的跨域访问。但是XDR对象和XHR对象相比还是有一些不同的。主要在以下几点。
1、cookie不会随请求发送,也不会随响应返回。
2、只能设置头部信息中的Content-Type字段。
3、不能访问头部响应信息。
4、只支持GET和POST请求。
注意:XDR对象open方法只有两个参数。请求的类型和URL.所有的XDR都是异步进行的。请求返回后,会触发onload事件,响应的数据保存在responseText中。例子如下:
1 domainRequest = new XDomainRequest(); 2 domainRequest.open(method, url);
this.domainRequest.send(null); 3 //注册相关回调函数 4 this.domainRequest.onload = callback; 5 this.domainRequest.onerror = failback;
注意:由于导致XDR失败的因素很多,建议使用onerror事件来处理异常。
其他浏览器对CORS的支持
Firefox 3.5+、Safiri 4+、Chrome、iOS系统的Safiri以及Android平台的WebKit都XMLHttpRequest对象实现了CORS的原生支持。在尝试打开不同来源的资源时,无需额外编写代码户可以触发这个行为。要请求位于另一个域中的资源,使用标准的XHR对象并在open中传入绝对路径即可。(http://.....)。
与IE中的XDR对象不同,通过跨域XHR对象可以访问status和statusText属性,而且还支持同步请求。跨域XHR对象也存在一些限制。如:
不能使用setRequestHander()设置自定义头部
不能发送和接收cookie
调用getAllResponseHeaders()总会返回空字符串
跨浏览器的CORS
各种浏览器对CORS的支持程度都不一样。但所有浏览器都支持简单的请求。下面通过代码来介绍下跨浏览器的CORS实现。如下:
1 /** 2 * 跨域版本的Ajax.在IE8-IE10中是使用XDomainRequest来实现CORS.注意:IE11不支持XDomainRequest 3 * 其他主流浏览器可以使用XMLHttpRequest来实现跨域 4 * method:调用ajax的方式:get/post 5 * url:调用服务端方法的路径(asmx,controller都可).如:Login/CheckUser注意get,post时不要使用?Id=XX&Name=XX等形式 6 **/ 7 function DomainAjaxInstance(method, url) { 8 var domainRequest = new XMLHttpRequest(); 9 if ("withCredentials" in domainRequest) { 10 // 此时即支持CORS的情况 11 // 检查XMLHttpRequest对象是否有"withCredentials"属性 12 // "withCredentials"仅存在于XMLHTTPRequest2对象里 13 domainRequest.open(method, url, true); 14 } 15 //IE8,9中跨域访问对象XDR 16 else if (typeof XDomainRequest != "undefined") { 17 domainRequest = new XDomainRequest(); 18 domainRequest.open(method, url); 19 } 20 else { 21 domainRequest = null; 22 } 23 //保存调用的相关参数 24 this.domainRequest = domainRequest; 25 this.method = method; 26 this.url = url; 27 } 28 29 /** 30 * 跨域访问(跨域访问支持仅GET和POST) 31 * data:调用方法时传递的参数.只需要以对象形式{Id:"",Name:""}传递data参数即可,无参传递null 32 * callback:获取结果的回调函数 33 * failback:跨域调用出错时的回调函数 34 **/ 35 DomainAjaxInstance.prototype.call = function (data, callback, failback) { 36 if (!(callback instanceof Function) || !(failback instanceof Function)) { 37 alert("调用call方法必须指定callback和failback方法"); 38 return; 39 } 40 var query = null; 41 if (data !== null && (data instanceof Object)) { 42 query = appendParameters(data); 43 } 44 var type = this.method; 45 if (type.toLowerCase() == "post") { 46 this.domainRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 47 this.domainRequest.send(query); 48 } 49 else { 50 var url = this.url; 51 if (query !== null) { 52 url += (url.indexOf("?") == -1) ? "?" : "&"; 53 url += query; 54 } 55 this.domainRequest.send(null); 56 } 57 //注册相关回调函数 58 this.domainRequest.onload = callback; 59 this.domainRequest.onerror = failback; 60 }
其他跨域技术
JSONP
JSONP是填充式JSON或者参数式JSON的缩写,是一种应用JSON的新方法。JSONP看起来和JSON差不多,只是被包含在函数调用中的JSON。一般调用方式是这样:callback({"name":"Nicolas"})。
JSONP由两部分组成,回调函数和数据。回调函数是当响应到来的时应该在页面调用的函数。回调函数的名称一般都是在页面中指定的。数据就是传入回调函数的JSON数据。
下面展示一个典型的JSONP请求:
http://freegeiop.net/json/?callback=handlerRsoponse(回调函数名称是:handlerRsoponse)。
JSONP是通过动态<script>标签来使用的。使用时通过为<script>标签设定src属性是一个跨域的URL。这里的<script>标签与<img>标签类似,都有能力不限制的从其他域加载资源。
当然JSONP也存在不足,主要表现在下面两点
1、JSONP是从其他域加载代码执行的,如果其他域不安全,很可能会在响应中夹杂着恶意代码。所以使用JSONP时一定要确保安全可靠。
2、要确定JSONP请求是否失败并不容易,现阶段开发人员只能使用定时器来检测是否接收到响应。
相关资料
对于JSONP,下面两篇博文介绍的不错,推荐给大家。大家可以看看JQuery中怎么使用这种跨域技术的。