AJAX学习笔记2:XHR实现跨域资源共享(CORS)以及和JSONP的对比
1 前言:首先对参考文章作者表示感谢,你们的经验总结给我们这些新手提供了太多资源。 2 跨域问题的源头---同源策略在客户端编程语言中,如javascript和 ActionScript,同源策略是一个很重要的安全理念,它的目的是为了保证用户信息的安全,防止恶意的网站窃取数据。 很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。由此可见,"同源政策"是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。 那么什么叫相同域(同源),什么叫不同的域(不同源)呢?当两个域具有相同的协议(如http),相同的端口(如80),相同的host(如www.example.org),那么我们就可以认为它们是相同的域。比如 http://www.example.org/index....(默认端口号80可以省略)和http://www.example.org/sub/in...是同域,而http://www.example.org, https://www.example.org,http://www.example.org:8080, http://sub.example.org中的任何两个都将构成跨域。 目前,如果非同源,共有三种行为受到限制。 (1) Cookie、LocalStorage 和 IndexDB 无法读取。 (2) DOM 无法获得。 (3) AJAX 请求不能发送。 作为前端开发者,我们很多时候要做的是突破这种限制。 补充:同源策略还应该对一些特殊情况做处理,比如限制file协议下脚本的访问权限。本地的HTML文件在浏览器中是通过file协议打开的,如果脚本能通过file协议访问到硬盘上其它任意文件,就会出现安全隐患,目前IE8还有这样的隐患。 3 跨域方式JSONP[参考1]JSONP是JSON with Padding的简写,是应用JSON实现服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。 它的基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。 实例: //访问跨域src并将数据填入到script标签中的函数 function addScriptTag(src) { var script = document.createElement('script'); script.setAttribute("type","text/javascript"); script.src = src; document.body.appendChild(script); } //网页动态插入<script>元素,由它向跨源网址src发出请求,src中包含回调函数 window.onload = function () { addScriptTag('http://example.com/ip?callback=foo'); } //回调函数的参数默认是返回的数据 function foo(data) { console.log('Your public IP address is: ' + data.ip); }; /上面代码通过动态添加<script>元素,向服务器example.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的。/ JSONP的缺点 4 AJAX实现跨域[参考2]参考[2]中介绍了复杂HTTP请求,本文只包含简单的GET和POST请求。 4.1 HTTP headerHTTP header分为请求头部和响应头部,在发送XHR请求时,会发送以下请求头部: 使用 setRequestHeader()方法可以设置自定义的请求头部信息。这个方法接受两个参数:头部字段的名称和头部字段的值。要成功发送请求头部信息,必须在调用 open()方法之后且调用 send()方法之前调用 setRequestHeader()。 调用 XHR 对象的 getResponseHeader()方法并传入头部字段名称,可以取得相应的响应头部信息。而调用 getAllResponseHeaders()方法则可以取得一个包含所有头部信息的长字符串,这种格式化的输出可以方便我们检查响应中所有头部字段的名称。 测试实例: //创建请求对象 var request = createRequest(); if (request == null) { alert("Unable to create request"); return; } request.onreadystatechange = showSchedule; //使用DOM0级方法添加event handler,因为不是所有浏览器都支持DOM2;没有event对象,直接使用request对象 request.open("GET",selectedTab + ".html",true);//返回HTML片段 request.send(null); } function showSchedule() { if (request.readyState == 4) { if ((request.status >= 200 && request.status <= 300)|| request.status == 304) { //返回响应头部 document.getElementById("content").innerHTML = request.getAllResponseHeaders(); }else { document.getElementById("content").innerHTML =request.status; } } } 输出结果显示全部的响应头部: 4.2 CORS 涉及的头部IE10及以上、Firefox 3.5+、 Safari 4+、 Chrome、 iOS 版 Safari 和 Android 平台中的 WebKit 都通过 XMLHttpRequest对象实现了对 CORS 的原生支持。只要在open()方法的URL中使用绝对定位即可实现CORS。一般推荐在同域中使用相对URL,在跨域时使用绝对URL。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。 对于简单请求(GET和POST),浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段用来说明:本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。 Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8 上面的头信息之中,有三个与CORS请求相关的字段,都以Access-Control-开头。详细解释如下: 4.3 测试实例把上例中的open()方法改成如下所示,URL指向其他域中的一个JavaScript文件,测试结果表明可以成功加载该文件。 使用FireFox的FireBug观察上述请求的响应过程,对应的请求头部:Origin是http://localhost 响应头部:access-control-allow-origin:*代表任意源都可以访问。 跨域使用的XHR对象可以访问status和statusText属性,而且支持同步请求,虽然这没多大用处。限制是不能使用 setRequestHeader()设置自定义头部;不能发送和接收 cookie;调用 getAllResponseHeaders()方法总会返回空字符串。 5 特殊的IE: XDR对象实现跨域对于XHR2,IE浏览器的支持是IE10以上。但是IE早在IE8时就推出了 XDomainRequest 对象进行跨域操作,一直沿用到IE10才被取代掉。因此在IE8,IE9中应该使用 XDomainRequest(XDR)来实现。XDR有以下几个特点: XDR 对象的使用方法与 XHR 对象非常相似,也是创建一个 XDomainRequest 的实例,调用 open()方法,再调用 send()方法。但与 XHR 对象的 open()方法不同, XDR 对象的 open()方法只接收两个参数:请求的类型和 URL。 var xdr = new XDomainRequest(); xdr.onload = function(){ alert(xdr.responseText); }; xdr.open("get","http://www.somewhere-else.com/page/"); xdr.send(null); 在请求返回前调用 abort()方法可以终止请求: xdr.abort(); //终止请求 与 XHR 一样, XDR 对象也支持 timeout 属性以及 ontimeout 事件处理程序。 6 跨浏览器的跨域解决方案即使浏览器对 CORS 的支持程度并不都一样,但所有浏览器都支持简单的(非 Preflight 和不带凭据的)请求,因此有必要实现一个跨浏览器的方案。检测 XHR 是否支持 CORS 的最简单方式,就是检查是否存在 withCredentials 属性。再结合检测 XDomainRequest 对象是否存在,就可以兼顾所有浏览器了。 function createCORSRequest(method,url){ var xhr = new XMLHttpRequest(); if ("withCredentials" in xhr){ xhr.open(method,url,true); } else if (typeof XDomainRequest != "undefined"){ vxhr = new XDomainRequest(); xhr.open(method,url); } else { xhr = null; } return xhr; } Firefox、 Safari 和 Chrome 中的 XMLHttpRequest 对象与 IE 中的 XDomainRequest 对象类似,都提供了共同的属性/方法如下: var request = createCORSRequest("get","http://www.somewhere-else.com/page/"); if (request){ request.onload = function(){ //对 request.responseText 进行处理 }; request.send(); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- lpc1788移植u-boot-2010.03之spi flash移植
- XML学习小结
- C#:如何从LINQ的IDictionary>集合中删除项目?
- 用于Ruby中过去时的ActiveSupport变形库?
- api – XML-RPC与REST
- cocos2d中去掉微信呢称中的表情字符(ios与android)
- laravel – 请求/autodiscover/autodiscover.xml导致TokenM
- xcode – ViewController.Type没有名为的成员
- flex的tree动态加载大量数据与滚动条相关的问题
- objective-c – 相当于clipsToBounds的Sprite Kit: