最近做的一個專案中需要ajax跨域取得數據,中間出了點小差錯,於是把ajax複習一下,記錄下來關於跨域的問題的要點,分享給大家
1.Ajax
1.1.Ajax簡介
Ajax簡介這一部分我們主要是談一下ajax的起源,ajax是什麼?因為這些都是跟技術無關的。所以,大多細節都是一筆帶過。
Ajax的起源?
Ajax一詞源自於2005年Jesse James Garrett發表的一篇題為"Ajax:A new Approach to Web Applications".他在這篇文 章中介紹了一種新技術,並用他的話說,就是Ajax :Asynchronous JavaScript XML的縮寫。
Ajax是什麼?
這種新技術的主要目的就是為了讓前端網頁能夠向伺服器請求額外的資料而不需要卸載頁面。自從這種技術出現以後,微軟率先引進XHRt物件(ajax能夠實現的核心物件),然後其他瀏覽器相繼實作此技術。總而言之,ajax就是一種能非同步通訊的技術。
1.2.Ajax的核心物件---XMLHttpRequest
# 因為IE5是最早引入這個XHR物件的,當時並沒有事實上的標準。在IE中有三種不同的XHR物件版本:MSXML2.XMLHttp,MSXML2.XMLHttp.3.0和MSXML2.XMLHttp.6.0;
#根據這三種版本號,在IE中建立一個XHR物件如下:
function createXHR() { //IE7之前的版本通过这种方式 var versions = [ 'MSXML2.XMLHttp', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp.6.0' ]; var xhr = null; for (var item in versions) { try { xhr = new ActiveXObject(item); //若不存在该版本,可能会出错 if (xhr) break; } catch (e) { //一般对这种错误不做处理 } } return xhr; }
在IE引進這個物件之後,其他瀏覽器廠商也相繼跟隨,這時候XHR物件成為事實上的標準!
跨瀏覽器建立XHR物件;
function createXHttpRequest() { if (typeof XMLHttpRequest !== 'undefined') { //不要用 if(XMLHttpRequest){}这种形式, return new XMLHttpRequest(); //如果是这种形式在找不到XMLHttpRequest函数的情况下,会报错。 } else if (typeof ActiveXObject !== 'undefined') { return createXHR(); //用到刚才我们创建的函数 } else { throw new Error('不能创建XMLHttpRequest对象'); } }
1.2.XMLHttpRequest的用法
XMLHttpRequest物件的函數有6個:
open("method",url,boolean); //该方法的三个参数,分别为----提交方式"get"或者"post"等 //&& url是相对于执行代码的当前页面的路径(使用绝对路径是允许的)&&是否异步 send(); //这个方法接收一个参数,这个参数是作为请求主体发送的数据, //说明: 如果有参数,请使用post方式提交 使用方式如下,send("user="+username+"&pwd="+password); //如果没有参数,为了兼容性考虑,必须在参数中传入null,即send(null);该方式使用get方式提交 abort(); //取消当前响应,关闭连接并且结束任何未决的网络活动。 //这个方法把 XMLHttpRequest 对象重置为 readyState 为 0 的状态,并且取消所有未决 //的网络活动。例如,如果请求用了太长时间,而且响应不再必要的时候,可以调用这个方法。 getResponseHeader() //返回指定的 HTTP 响应头部的值。其参数是要返回的 HTTP 响应头部的名称。可以使用任 //何大小写来制定这个头部名字,和响应头部的比较是不区分大小写的。 //该方法的返回值是指定的 HTTP 响应头部的值,如果没有接收到这个头部或者 readyStat //e 小于 3 则为空字符串。如果接收到多个有指定名称的头部,这个头部的值被连接起来并 //返回,使用逗号和空格分隔开各个头部的值。 getAllResponseHeaders() //把 HTTP 响应头部作为未解析的字符串返回。 //如果 readyState 小于 3,这个方法返回 null。否则,它返回服务器发送的所有 HTTP 响应的 //头部。头部作为单个的字符串返回,一行一个头部。每行用换行符 "\r\n" 隔开。 setRequestHeader() //向一个打开但未发送的请求设置或添加一个 HTTP 请求。
XMLHttpRequest物件的屬性有5個:
屬性 描述
responseText 作為回應主體被傳回的文字
responseXML 如果相回應的是text/html或是application/xml類型的話,這個屬性將會保存著回應的XML文件
status http的回應狀態碼
statusText http狀態的說明
readyState XMLHttpRequest物件的狀態位元0 1 2 3 4 分別表示5種狀態
timeout 設定逾時時間,單位是ms.目前只有IE8 支援---尚未標準化(不建議使用)
XMLHttpRequest物件的事件屬性onReadyStateChange:-----所有瀏覽器相容
# 此屬性監聽的是 XMLHttpRequest物件的readyState屬性的變更:
readyState的變更分別對應如下狀態:
0:尚未初始化。未呼叫open()之前
1:啟動。呼叫open()之後,但是未呼叫send();
2:傳送。呼叫send()但是尚未得到回應。
3:正在接收資料。剛接收到回應資料開始到接收完成之前。
4: 完成。資料接收完成。
xhr.onreadystatechange = function () { if (xhr.readyState == 4) { if (xhr.status >= 200 && xhr.status <== 300 || xhr.status == 304) { alert(xhr.responseText); //处理接收的数据 } else { //请求失败,未得到响应数据 } } }; //补充说明:注册事件必须发生在send()以前
XMLHttpRequest对象的事件属性ontimeout -----仅限IE8+,不过最新的主流高版本浏览器也已经实现(不推荐使用) xhr.timeout=1000;//一秒钟 xhr.ontimeout=functon(){ //处理代码 ......}
這種使用方式有個問題需要注意,就是在逾時之後,在接收到資料後仍然會觸發onreadystatechange事件,如果在處理onreadychange事件時存取xhr.status屬性,會出錯。所以我們在存取該屬性時需要做一下try{}catch處理。但是,因為這個屬性暫不相容,所有我們就不重點講了。
XMLHttpRequest對象的事件屬性onload onerror onloadstar onbort onprogress:
-----非IE瀏覽器和IE 10 已實現
onload在IE8以上可以實現,大部分事件根據readySate變化均可實現,以上事件只不過是方便使用而已。
onload和onprogress 這兩個事件分別對應著readyState=4和readyState=3的情況,使用方式分別如下:
xhr.onload= function (event) { //event只包含一个属性 event.target=xhr;使用方式只是在readyState=4时差不多.. } xhr.onprogress=function(event){ //event除了包含event.target=xhr之外,还包含三种属性 //lengthComputale(进度信息是否可用),position(已接受字节数)和totalSize(总字节数). }
補充:有些事件都可以根據readyState的狀態進行模擬。只有有的瀏覽器進行了方便化處理而已。
3.單向跨域技術---CORS
今天我們這裡講的是客戶端網頁向不在同一個域的伺服器請求資料..客戶端在收到傳回的資料時,用回呼函數處理資料。
即:
1. 用戶端向網域外伺服器請求資料
2.伺服器得到回應後傳送資料至客戶端。
3.客戶端根據傳回的資料執行回呼函數.
我知道不同域下的iframe也可以进行通信,而且这也是一种跨域通信技术。但是,这种iframe页面之间的双向通信,我们在下一个专题里面讲解,今天主要讲的是单向通信。
3.1.CORS跨域请求的原理
在用xhr(XMLHttpRequest)对象或者xdr(XDomainRequest)对象,发送域外请求时,大概的实现原理如下图:
3.2.IE中CORS技术的实现
IE8引入了一个XDR类型,这个类型与XHR基本类似,但是其能实现安全可靠地跨域通信。
XHD的特点:
1.cookie不会随请求发送,也不会随响应返回。
2.只能设置请求头部中的Content-Type片段。
3.不能访问响应头部信息。
4.只是支持get和post请求。
XDR支持onload和onerror事件属性,且其使用方式和XHR基本一致,不过其open()只接收两个参数,默认是异步的。
var xdr = new XDomainRequest(); xdr.onload = function () { //处理xdr.responseText } xdr.onerror = function () { }; xdr.open('get', '绝对url'); xhr.send(null);
3.3.跨浏览器的CORS技术实现
在标准浏览器中XHR对象就已经可以自动实现跨域请求,但是XHR和XDR的不同之处:
1.XHR可以在设置 withCredentials =true时,浏览器会把cookie发送给服务器,服务器此时通过设置头部Access-Control-Allow-Credentials:true时来响应。如果,服务器不设置这个属性,则浏览器会触发onerror事件。
2.在回调函数中可以访问status和statusText属性,而且支持同步请求。
以下是实现跨域请求的代码:
function createCrosRequest(method, url) { var xhr = new XMLHttpRequest(); //IE7+ if ('withCredentials' in xhr) { //IE8-IE9浏览器没有这个属性 xhr.open(method, url, true); } else if (typeof XDomainRequest != 'undefined') { xhr = new XDomainRequest(); //IE xhr.open(method, url) } return xhr; } var request=CreateCrosRequest("get","url"); if(request){ request.onload=function(){ //处理request.responseText; } request.send(null); }
4.单向跨域技术 ---JSONP技术
JSONP技术比较简单,其主要原理主要是利用script标签的特性。
script标签和image标签一样,它们都具有src属性,而且这个属性是可跨域的。
因为script标签返回的都是js代码,且该js代码会自动执行。所以,如果我们请求返回的数据也是类似一段js代码的形式,岂不是就可以实现在脚本加载完毕后自动执行。
如果我们的请求,返回的数据是 callback + '(' + json + ')'; 这种形式的数据, 那么在脚本加载完毕之后也就能自动执行callback()函数了.
4.1.客户端写法
<!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> </head> <body> <button id="button">请求数据</button> </body> <script> window.onload=function(){ var button=document.getElementById("ibutton"); function callback(data){ //处理data } button.onclick=function(){ var script=document.createElement("script"); script="http://www.sasd.com/json/?callbak=callback"; document.body.insertBefore(script,document.body.firstChild);//加载脚本 } } </script> </html>
1.客户端将回调函数名写入