- 瀏覽器在XMLHttpRequest類上定義了他們的HTTP API
- XMLHttpRequest類的每個實例表示了一個獨立的請求/相應對
- HTTP請求包括:HTTP請求方法(post還是get),請求URL,一個可選的請求頭集合,一個可選的請求主體
- HTTP響應包括:一個數字或者文字組策划過的狀態嗎(顯示請求的成功失敗),一個響應頭集合,響應主體
- 步驟:
- 實例化XMLHttpRequest對象 :
var request=new XMLHttpRequest();
2.指定請求
request.open("method","url");
第一個參數:指定HTTP方法或動作,“GET”適用於當URL完全猴子腚請求資源,當請求對服務器沒有任何副作用以及當服務器的響應是可緩存的。
“POST"常用語form表單,請求主體中包含額外數據(表單數據)且這些數據常存儲到服務器上的數據庫中(副作用)
第二個參數:url,如果使用絕對url,協議主機和端口常匹配所在文檔的對象內容:跨域的請求常常會報錯
3.設置請求頭
request.setRequestHeader(“Content-Type”,"text/plain");
對相同請求頭調用多次setRequestHeader並不會覆蓋,而是賦予多次的值。
/*以post方式發送數據給服務器*/ function postMessage(msg){ var request=new XMLHttpRequest(); request.open("post","handlerPost"); request.setRequestHeader('Content-Type','text/plain;charset=UTF-8'); request.send(msg); }
一個HTTP響應:狀態嗎,響應頭集合,響應主體。
XMLHttpRequest對象的屬性和方法中可以獲得以上值:
1.status和statusText屬性以數字和純文本的格式趕回HTTP狀態碼。200:請求成功;404:URL不能匹配服務器上任何資源。
2.getResponseHeader()和getResponseHeaders()查詢響應頭
3.響應主體沖responseText屬性中得到文本形式,從responseXML得到Document形式
XMLHttpRequest對象通常異步使用:發送請求之后,send()方法立即返回,之后再返回響應。為了在響應准備就緒是得到通知,必須使用readystatechange事件。
XMLHttpRequest的readyState屬性指定了HTTP請求的狀態,4:響應完成,3:接收到響應主體,2:接受到頭信息。
//postMessage("This is post message!"); //發送一個HTTP GET請求以獲得指定URL內容 //當響應成功到達,驗證它是否是純文本 //如果是,把它傳遞給指定回調函數 function getText(url,callback){ var request=new XMLHttpRequest(); request.open("GET",url); request.onreadystatechange=function(){ //如果請求完成,則它是成功的 if(request.readyState===4 && request.status===200){ var type=request.getResponseHeader("Content-Type"); if(type.match(/^text/)) //確保相應是文本 callback(request.responseText); } } }
同步響應:
//同步響應 //發起同步的HTTP GET請求以獲得指定URL的內容 //返回響應文本,或如果請求不成功或響應不是文本就報錯。 function getTextSync(url){ var request=new XMLHttpRequest(); request.open("GET",url,false); //把fasle作為第三個參數傳遞給open(),那么send()方法將阻塞直到請求完成 request.send(null); if(request.status!==200) throw new Error(request.statusText); var type=request.getResponseHeader('Content-Type'); if(!type.match(/^text/)) throw new Error("Expected textual respomse;got:"+type); return request.responseText; }
響應解碼
///發送HTTP GET響應以獲取指定URL內心哦那個 //當響應到達時候,把它解析后的XML Document對象,解析后的JSON對象或字符串形式傳遞給回調 function get(url,callback){ var request=new XMLHttpRequest(); request.open('GET',url); request.onreadystatechange=function(){ if(request.readyState===4 && request.status===200){ var type=request.getResponseHeader("Content-Type"); if(type.indexOf("xml") !== -1 && request.responseXML){ callback(request.responseXML); //Document對象響應 }else if(type=== 'application/json'){ callback(JSON.parse(request.responseText)) //JSON響應 }else { callback(request.responseText); } } }; request.send(null); }
不用考慮特殊編碼的另一個相應類型application/javascript亦或text/javascript,如果你想使用XMLHttpRequest請求Javascript腳本,然后使用eval()的話,因為<script>能發起跨域請求(JSONP就是運用了這一點),但是XMLHttpRequest API則禁止。
web服務器也可使用二進制數據(例如,圖片文件)相應HTTP請求。
//使用XMLHttpRequest下載Blob //以Blob的形式獲取URL指定的內容,並將其傳遞給指定的回調函數 function getBlob(url,callback){ var xhr=new XMLHttpRequest(); //創建一個新的XHR對象 xhr.open("GET",url); //指定要獲取內容的URL xhr.responseType="blob"; //以BLOB形式 xhr.onload=function(){ //onload比onstatechange更容易 callback(xhr.response); //注意,這里是response不是.responseText }; xhr.send(null); }
服務器響應的正常解碼是建立在服務器端發送的"Content-type"和MIME類型上的。
如果服務器端發送過的是XML文檔,但是設置“Content-type”錯誤,將導致XMLHttpRequest對象將使用錯誤的編碼來相應。
所以overrideMimeType方法將重寫服務端返回的MIME類型,注意該方法要在send()之前調用
//無論服務器端設置的Content-type是什么類型,豆漿把它當作文本類型來處理 request.overrideMimeType("text/plain;charset=utf-8");
表單編碼的請求
//將對象{find:"pizza",zipcode:01234,radius:"1km"}變成表單編碼的格式 find=pizza&zipcode=01234$radius=1km 的形式 function encodeFormData(data){ if(!data) return ""; var pairs=[]; for(var name in data){ if(!data.hasOwnProperty(name)) continue; //跳過繼承方法 if(typeof data[name] === "function") continue;//跳過方法 var value=data[name].toString(); name=encodeURIComponent(name.replace("%20","+")); value=encodeURIComponent(value.replace("%20","+")); pairs.push(name+"="+value); } return pairs.join('&'); }
HTTp POST請求包含一個請求主體,它包含客戶端傳遞給服務端的數據。表單數據編碼格式的正確MIME類型:
當POST方法提交這種順序的表單數據時,必須設置“Content-type”請求頭為這個值。
//使用表單數據發起一個HTTP POST請求 function postData(url,data,callback){ var request=new XMLHttpRequest(); request.open("POST",url); request.onreadystatechange=function(){ if(request.readyState===4 && callback) callback(request); }; request.setRequestHeader("Content-Type","application/x-www-form-urlencoded"); request.send(encodeFormData(data)); //發送表單編碼數據 }
HTTP GET請求沒有請求主體:
//使用表單編碼數據發起get請求 function getData(url,data,callback){ var request=new XMLHttpRequest(); request.open("GET",url+"?"+encodeFormData(data)); request.onreadystatechange=function(){ if(request.readyState===4 && callback) callback(request); }; request.send(null); }
JSON編碼請求
//使用JSON編碼主題發起HTTP POST請求 function postJSON(url,data,callback){ var request=new XMLHttpRequest(); request.open("POST",url); request.onreadystatechange=function(){ if(request.readyState===4&&callback) callback(request); }; request.setRequestHeader("Content-Type","application/json"); request.send(JSON.stringify(data)); }
HTTP進度事件
在XHR2規范中,XMLHttpRequest對象在請求的不同階段將觸發不同類型的事件,所以不再需要檢查readyState屬性
當調用send()事件時,觸發單個loadstart事件,當正在加載服務器的相應時,XMLHttpRequest對象會發生progress事件,通常每隔50毫秒左右,所以可以使用這些事件給用戶反饋請求進度。如果請求快速完成,它可能不會觸發progress事件。當事件完成時,觸發load事件。
一個完成的請求不一定是成功的請求,例如:load事件的處理程序應該檢車XMLHttpRequest對象的status狀態來確定收到的是“200 OK”而不是“400 Not Found”的HTTP響應。
HTTP請求無法完成有三種情況,對應三種事件
1.請求超時:timeout時間
2.請求中止:abort事件
3.阻止請求完成:比如太多重定向這樣的網絡錯誤:error事件
對於任何具體的請求,瀏覽器只會觸發load,abort,timeout和error事件中的一個,一旦這些事件觸發后,將觸發loadend事件。需要在send()之前進行事件監聽,下面是下載事件被觸發:
var req = new XMLHttpRequest(); req.addEventListener("progress", updateProgress, false); req.addEventListener("load", transferComplete, false); req.addEventListener("error", transferFailed, false); req.addEventListener("abort", transferCanceled, false); req.open(); ... // progress on transfers from the server to the client (downloads) function updateProgress(evt) { if (evt.lengthComputable) { var percentComplete = evt.loaded / evt.total; ... } else { // Unable to compute progress information since the total size is unknown } } function transferComplete(evt) { alert("The transfer is complete."); } function transferFailed(evt) { alert("An error occurred while transferring the file."); } function transferCanceled(evt) { alert("The transfer has been canceled by the user."); }
下面是上傳事件被觸發:
var req = new XMLHttpRequest(); req.upload.addEventListener("progress", updateProgress); req.upload.addEventListener("load", transferComplete); req.upload.addEventListener("error", transferFailed); req.upload.addEventListener("abort", transferCanceled); req.open();
判斷是否支持progress事件
if("onprogress" in (new XMLHttpRequest())){ //支持progress事件 }
和progress事件相關聯的事件對象上有三個有用的屬性
1.loaded屬性是目前傳輸的字節數值
2.total屬性是自"Content-Length"頭傳輸的數據的整體長度(單位字節)
3.知道上面的長度則lengthComutable為true,反之亦然。
request.onprogress=function(e){ if(e.lengthComputable){ progress.innerHTML=Math.round(100* e.loaded/ e.total)+"% Complete"; } };
中止請求和超時
///實現超時 //如果響應成功到達,傳入responseText給回調函數 //如果響應在timeout毫秒內沒有到達,中止這個請求 //瀏覽器可能在abort()后出發“readystatechange” //如果是部分請求結果到達,設置可能設置status屬性 //所以需要設置一個標記,當部分且超時的響應到達時不會調用回調函數 ///如果使用load事件就沒有這個風險 function timedGetText(url,timeout,callback){ var request=new XMLHttpRequest(); var timedout=false; //啟動計時器,在timeout毫秒之后將終止請求 var timer=setTimeout(function(){ timedout=true; request.abort(); }, timeout); request.open("GET",url); request.onreadystatechange = function(){ //定義事件處理程序 if(request.readyState !== 4) return; //忽略未完成的請求 if(timedout) return; //忽略終止請求 clearTimeout(timer); //取消等待的超時 if(request.status === 200) //如果請求成功 callback(request.responseText); //把response傳給回調 }; request.send(null); }
跨域請求
作為同源策略的一部分,XMLHttpRequest對象通常僅可以發起和文檔具有相同服務器的HTTP請求,這個限制關閉了安全漏洞,但是同時也阻止了大量適合使用的跨域請求。
CORS支持的跨域請求工作不需要做任何的事情,但是安全細節要了解
1.給open()方法傳遞的用戶名和密碼將絕對不會通過跨域請求發送
2.跨域請求不會包含其他的任何用戶證書:cookie和HTTP身份驗證令牌(token)通常不會作為請求內容發送

1 /* 2 * 3 * linkdetails.js 4 * 5 * 這個常見的javascript模塊查詢有href屬性但是沒有title屬性的所有<a>元素 6 * 並給他們注冊onmouseover事件處理程序 7 * 這個時間處理程序使用XMLHttpRequest HEAD請求取得鏈接資源的詳細信息 8 * 然后把這寫詳細信息設置成鏈接的title屬性 9 * 這樣他們會在工具提示中顯示 10 * */ 11 whenReady(function(){ 12 //是否有機會使用跨域請求? 13 var supportsCORS= (new XMLHttpRequest()).withCredentials !== undefined; 14 15 //遍歷文檔中的所有鏈接 16 var links=document.getElementsByTagName('a'); 17 for(var i=0;i<links.length;i++){ 18 var link=links[i]; 19 if(!link.href) continue; //跳過沒有超鏈接的錨點 20 if(link.title) continue; //跳過已經有工具提示的鏈接 21 //如果這是一個跨域鏈接 22 if(link.host !== location.host || link.protocol !== location.protocol){ 23 link.title="站外鏈接"; //假設我們不能得到任何信息 24 if(!supportsCORS) continue; //如果沒有CORS支持就退出 25 //否則,我們能了解這個鏈接的更多信息 26 //所以繼續前行,注冊事件處理程序,於是我們可以嘗試 27 } 28 //注冊事件處理程序,當鼠標懸停時下載鏈接詳細信息 29 if(link.addEventListener) 30 link.addEventListener("mouseover",mouseHandler,false); 31 else 32 link.attachEvent("onmouseover",mouseHandler); 33 } 34 35 function mouseHandler(e){ 36 var link = e.target || e.srcElement; //<a>元素 37 var url=link.href; //鏈接url 38 39 var req=new XMLHttpRequest(); //新請求 40 41 req.open("HEAD",url); //僅僅詢問頭信息 42 req.onreadystatechange=function(){ 43 if(req.readyState !== 4) return; //忽略未完成的請求 44 if(req.status === 200){ //如果成功 45 var type=req.getResponseHeader("Content-Type"); //獲取鏈接的詳細信息 46 var size=req.getResponseHeader("Content-Length"); 47 var date=req.getResponseHeader("Last-Modified"); 48 49 //在工具提示中顯示詳細信息 50 link.title="類型:"+type+"\n"+ 51 "大小:"+size+"\n"+"時間:"+date; 52 }else{ 53 //如果請求失敗,且鏈接沒有“站外鏈接”的工具提示,那么顯示這個錯誤 54 //if(!link.title) 55 link.title="不能獲取詳情:\n"+ 56 req.status+" statusText"+req.statusText; 57 } 58 }; 59 req.send(null); 60 61 //移除處理程序,僅想要一次獲取這些頭信息 62 if(link.removeEventListener) 63 link.removeEventListener("mouseover",mouseHandler,false); 64 else 65 link.detachEvent("onmouseover",mouseHandler); 66 } 67 });
出現的bug
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
XMLHttpRequest2 進行跨域訪問時需要服務器許可,不是任何域都接受跨域請求的。先來看一下從 Yahoo YQL 域返回的響應頭(Response Header ):注意:
1 HTTP/1.1 200 OK
2 Set-Cookie: AO="o=1&s=1&dnt=1"; Version=1; Domain=yahoo.com; Max-Age=630720000; Expires=Sat, 18-Jun-2033 10:07:41 GMT; Path=/
3 Access-Control-Allow-Origin: *
4 Cache-Control: public, max-age=899
5 Content-Type: text/xml;charset=utf-8
6 Content-Encoding: gzip
7 Vary: Accept-Encoding 8 Date: Sun, 23 Jun 2013 10:07:40 GMT
注意里面有一條標識 Access-Control-Allow-Origin:* ,這就表示允許跨域訪問,所以可以正常訪問該域,而對於其他沒有該標識的域就會出現禁止訪問提示。
解決方法:
1.在服務器端設置response頭,php是<?php header('Access-Control-Allow-Origin: *'); ?>,當然會讓你的服務器網站變得很危險
2.在chrom瀏覽器中設置
1)安裝Chrom插件(你得先翻了牆( ̄▽ ̄)"):點擊這里
2)在windows中window+r打開運行,輸入
chrome.exe --user-data-dir="C:/Chrome dev session" --disable-web-security
還要注意的一點是:chrome不支持本地運行Access-Control-Allow-Origin,所以為了可以讓chrome瀏覽器在頭部發送Access-Control-Allow-Origin,你必須結合你的hosts文件將localhost指定成其他域名:127.0.0.1 localhost yourdomain.com
JSONP
function foo(data) { // do stuff with JSON } var script = document.createElement('script'); script.src = '//example.com/path/to/jsonp?callback=foo' document.getElementsByTagName('head')[0].appendChild(script);