學過Ajax的都知道,Ajax與服務器異步交互的核心便是XMLHttpRequest,有了XMLHttpRequest才使的Ajax有了與后交互的能力,今天就來全面的回顧下XMLHttpRequest(ajax的其他組成元素:DOM,Javascript,css等這里就不介紹了)可以看http://www.cnblogs.com/shenliang123/archive/2012/04/25/2470244.html
1.首先介紹XMLHttpRequest對象的方法:
(1)abort---------------------------------停止發送當前請求
(2)getAllResponseHeaders---------------獲得服務器返回的所有響應頭
(3)getResponseHeader("headerLabel")---根據響應頭的名字來獲取相應的響應頭
(4)open("method", "URL"[,asyncFlag[,"userName",[,"password"]]])
------------------建立與服務器的URL的連接,並設置請求的的方法,以及是否使用異步請求,如果遠程服務需要用戶名和密碼,則提供相應的用戶名和密碼
(5)send(content)------------------------發送請求,其中content是請求的參數,如果不需要傳遞參數就將其設為null,在get方式提交時參數拼接在URL后面的,因此就將content設為null,不過以post方式提交也可以將參數拼接在URL后面的,此時也將content設為null,但也可以將參數放到content中進行傳遞
(6)setRequestHeader("label","value")----該方法一般是在post方式提交的時候用到的,post提交請求前需要先設置請求頭
2.無論是何種請求,使用XMLHttpRequest進行連接都應該按照如下步驟:
(1)初始化XMLHttpRequest對象,需要根據不同的瀏覽器進行不同的創建,因此首先需要判斷瀏覽器的類別
(2)打開與服務器的連接(使用open("method", "URL"[,asyncFlag[,"userName",[,"password"]]]))。打開連接時,指定發送請求的方法:采用get或post;指定是否以異步方式(true為采用異步)
(3)設定監聽XMLHttpRequest狀態改變的事件處理函數(即設定的回調函數)
(4)發送請求(使用send(content))
3.XMLHttpRequest對象常用的屬性:
(1)onreadystatechange-------------------用於指定XMLHttpRequest對象狀態改變時的事件處理函數。onreadystatechange屬性的作用與按鈕對象的onclick屬性一樣,
它們都是事件處理屬性。即XMLHttpRequest是事件源,它可以引發readystatechange事件,當程序將一個函數引用賦給XMLHttpRequest對象的readystatechange屬性,
如:objXMLHttp.onreadystatechange = processResponse;processResponse函數即成為XMLHttpRequest對象的事件處理器,每次XMLHttpRequest對象的狀態
改變都會觸發監聽該事件的事件處理器,因此我們需要在事件處理器即函數中進行正當的判斷來實現,具體的操作見代碼
XMLHttpRequest對象的幾種狀態:
---> 0 ---------------------XMLHttpRequest對象還沒有完成初始化
---> 1 ---------------------XMLHttpRequest對象開始發送請求
---> 2 ---------------------XMLHttpRequest對象的請求發送完成
---> 3 ---------------------XMLHttpRequest對象開始讀取服務器的響應
---> 4 ---------------------XMLHttpRequest對象讀取服務器響應結束
以上的狀態就是通過下面的readyState屬性來進行讀取的
(2)readyState----------------------------XMLHttpRequest對象的處理狀態
(3)responseText-------------------------用於獲取服務器的響應文本
(4)responseXML-------------------------用於獲取服務器端響應的XML文檔對象
(5)status--------------------------------該屬性是服務器返回的狀態文本信息,只有當服務器的響應已經完成(即readyState==4),才會有這個狀態碼
服務器常用的狀態碼和對應的含義如下:
---> 200 -------------------服務器響應正常
---> 304 -------------------該資源在上次請求之后沒有任何修改,這個通常用於瀏覽器的緩存機制,我們為了在請求時放在讀取緩存一般會在url地址上拼接上一個時間戳來騙過瀏覽器
---> 400 -------------------無法找到請求的資源
---> 401 -------------------訪問資源的權限不足
---> 403 -------------------沒有權限訪問資源
---> 404 -------------------需要訪問的資源不存在
---> 405 -------------------需要訪問的資源被禁止
---> 407 -------------------訪問的資源需要代理身份驗證
---> 414 -------------------請求的url太長
---> 500 -------------------服務器內部錯誤
(6)statusText----------------------------該屬性是服務器返回的狀態文本信息(與status對應),只有當服務器的響應已經完成,才會有這個狀態文本信息
下面演示完整的ajax交互
頁面:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <script type="text/javascript" src = "js/XMLHttpRequestTest.js"></script> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>AjaxTest.html</title> <body> <input type = "text" name = "content" id = "content" > <input type = "button" name = "get" value = "GET發送" onclick = "getSend()"> <input type = "button" name = "post" value = "POST發送" onclick = "postSend()"> </body> </html>
AJAX:
var objXMLHttp; /** * 進行createXMLHttpRequest對象的創建,由於不同的瀏覽器廠商對於XMLHttpRequest的支持不一樣,因此創建的時候需要根據不同的瀏覽器進行創建 * */ function createXMLHttpRequest(){ //對於Firefox,Opera等遵守DOM 2規范的瀏覽器 if(window.XMLHttpRequest){ objXMLHttp = new XMLHttpRequest(); } //對於IE瀏覽器 else{ //將IE瀏覽器不同的XMLHttp實現聲明為數組 var MSXML = ['MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP']; //依次對每個XMLHttp創建XMLHttpRequest對象 for(var i = 0; n< MSXML.length; i++){ try{ //微軟發布的是ActiveX控件 objXMLHttp = new ActiveXObject(MSXML[i]); //如果正常創建XMLHttpRequest對象就使用break跳出循環 break; }catch(e){ alert("創建XMLHttpRequest對象失敗"); } } } } /** * 通過post方式提交 * */ function postSend(){ var value = document.getElementById("content").value; alert(value); //初始化XMLHttpRequest對象 createXMLHttpRequest(); //創建請求的URL var url = "ajaxServlet" //打開與服務器的連接,使用post方式 objXMLHttp.open("POST", url, true); //post方式需要設置請求消息頭 objXMLHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); //設置處理響應的回調函數 objXMLHttp.onreadystatechange = processResponse; //發送請求並設置參數,參數的設置為param=value的形式 objXMLHttp.send("value="+value); } /** * 通過GET請求 * */ function getSend(){ var value = document.getElementById("content").value; //alert(value); //初始化XMLHttpRequest對象 createXMLHttpRequest(); alert("創建成功"); //創建請求的URL,get方式采用url拼接參數 var url = "ajaxServlet?value="+value; objXMLHttp.open("GET", url, true); //設置處理響應的回調函數 objXMLHttp.onreadystatechange = processResponse; objXMLHttp.send(null); } /** * 設定的回調函數 * */ function processResponse(){ //響應完成且響應正常 if(objXMLHttp.readyState == 1){ alert("XMLHttpRequest對象開始發送請求"); }else if(objXMLHttp.readyState == 2){ alert("XMLHttpRequest對象的請求發送完成"); }else if(objXMLHttp.readyState == 3){ alert("XMLHttpRequest對象開始讀取服務器的響應"); }else if(objXMLHttp.readyState == 4){ alert("XMLHttpRequest對象讀取服務器響應結束"); if(objXMLHttp.status == 200){ //信息已經成功返回,開始處理信息 //先捕獲下所有的請求頭 var headers = objXMLHttp.getAllResponseHeaders(); alert("所有的請求頭= "+headers); //得到服務器返回的信息 var infor = objXMLHttp.responseText; alert("服務器端的響應 = "+infor); }else{ alert("所請求的服務器端出了問題"); } } }
服務器端:
package xidian.sl.ajax; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class AjaxServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); String value = request.getParameter("value"); System.out.println("value"+ value); out.print("得到的value為 = "+ value); } }
發現上面使用get方式進行提交時,如果輸入中文傳到服務器端就出現亂碼:
輸入:
然后點擊GET發送,然后查看服務器端打印到控制台的value:
原因:當使用GET方式發送請求時,請求的參數是拼接在url地址后面的,而根據http的傳輸方式,如果傳輸的參數為中文,就會編碼成url的格式進行傳遞,此時我們就要對其做點處理了:
(1).在服務器端進行編碼格式的轉變:先將參數按ISO-8859-1字符集編碼成字節數組,然后按UTF-8字符集將該字節數組解碼為字符串:
String param = new String(請求參數.getBytes("ISO-8859-1"), "UTF-8"),但這種方式並不是全能的,因為我們這里是將所有的參數都以UTF-8進行解碼,但不同的瀏覽器請求參數的編碼格式是不一樣的,不一定為UTF-8,如IE默認的編碼格式為GBK,需要改成new String(請求參數.getBytes("ISO-8859-1"), "GBK"),因此我們一般選擇第二種方式或第三種方式
(2).頁面端發出的數據做一次encodeURI,服務器端使用 new String(請求參數.getBytes("iso8859-1"),"utf-8")如:
var url= "AJAXServer?name="+encodeURI($("#userName").val() ) ;
(3)頁面端發出的數據做兩次encodeURI處理, 服務器端用URLDecoder.decode(請求參數,"utf-8");具體見:http://www.cnblogs.com/shenliang123/archive/2012/04/19/2456758.html
在POST請求時Ajax應用默認采用UTF-8字符集來編碼請求參數,在服務器端可以不使用request.setCharacterEncoding("UTF-8");
但養成良好的習慣還是建議寫request.setCharacterEncoding("UTF-8");
還有一個問題就是為了不讓瀏覽器讀取緩存,我們需要在url地址后拼接一個時間戳來騙過瀏覽器:
//給URL增加時間戳,騙過瀏覽器,不讀取緩存 function convertURL(url){ //獲取時間戳 var timstamp=(new Date()).valueOf(); //將時間戳信息拼接到URL上 if(url.indexOf("?")>=0){//用indexof判斷該URL地址是否有問號 url=url+"&t="+timstamp; }else{ url=url+"?t="+timstamp; } return url; }
詳細見:http://www.cnblogs.com/shenliang123/archive/2012/04/19/2456758.html
從返回的結果可以看出:返回的所有響應頭並不是組成一個數組,而是由“名:值”組成的鍵值對字符串
到此我們對於XMLHttpRequest了解的差不多了,但我們上面介紹的都是文本的請求,在回調函數中是通過responseText屬性來獲取服務器端返回的文本,下面我們要介紹發送
xml請求,這個適合於發送復雜的參數到服務器端,我們可以將客戶端頁面的參數封裝為xml字符串形式:
在上面的js中添加兩個方法:
/** * 創建xml文檔 * */ function createXML(){ //開始創建XML文檔,countrys是根元素 var xml = "<countrys>" //獲取country元素,並獲取其所有的子元素 var options = document.getElementById("country").childNodes; var option = null; //遍歷城市下拉列表的所有選項 for(var i = 0; i< options.length; i++){ option = options[i]; //判斷是否被選中 if(option.selected){ //在countrys節點下增加一個country子節點,這里需要對 / 進行轉義 xml = xml+"<country>"+option.value+"<\/country>"; } } //結束xml根節點 xml = xml+"<\/countrys>"; //返回 return xml; } /** * 使用xml進行傳遞 * */ function send(){ //初始化XMLHttpRequest對象 createXMLHttpRequest(); //創建請求的URL var url = "xmlServlet" //打開與服務器的連接,使用post方式 objXMLHttp.open("POST", url, true); //post方式需要設置請求消息頭 objXMLHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); //設置處理響應的回調函數 objXMLHttp.onreadystatechange = processResponseXML; //發送xml請求,此時參數的設置不再是param=value的形式進行發送,而是直接采用xml字符串作為參數 objXMLHttp.send(createXML()); } /** * xml請求的回調函數 * */ function processResponseXML(){ //響應完成且響應正常 if(objXMLHttp.readyState == 1){ alert("XMLHttpRequest對象開始發送請求"); }else if(objXMLHttp.readyState == 2){ alert("XMLHttpRequest對象的請求發送完成"); }else if(objXMLHttp.readyState == 3){ alert("XMLHttpRequest對象開始讀取服務器的響應"); }else if(objXMLHttp.readyState == 4){ alert("XMLHttpRequest對象讀取服務器響應結束"); if(objXMLHttp.status == 200){ //信息已經成功返回,開始處理信息 //先捕獲下所有的請求頭 var headers = objXMLHttp.getAllResponseHeaders(); alert("所有的請求頭= "+headers); //得到服務器XML相應,這里是通過responseXML屬性來獲得,這也是唯一區別的地方 var infor = objXMLHttp.responseXML; alert("服務器端的響應 = "+infor); }else{ alert("所請求的服務器端出了問題"); } } }
頁面端:就是一個可以多選的下拉框
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>XMLAjaxTestl.html</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script type="text/javascript" src = "js/XMLHttpRequestTest.js"></script> </head> <body> <!-- 添加 multiple = "multiple"屬性后下拉框可以為多選--> <select name = "country" id = "country" multiple = "multiple"> <option value = "1" selected = selected>浙江</option> <option value = "2">北京</option> <option value = "3">上海</option> </select> <input type = "button" name = "send" value = "發送" onclick = "send()"/> </body> </html>
服務器端的處理:
package xidian.sl.ajax; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.Iterator; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.XPPReader; public class XMLServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); StringBuffer buffer = new StringBuffer(); String line = null; //通過request獲取輸入流 BufferedReader reader = request.getReader(); //依次讀取請求輸入流中的數據 while((line = reader.readLine())!=null){ buffer.append(line); } //將從輸入流中讀取到的數據轉化為字符串 String xml = buffer.toString(); InputStream is = new ByteArrayInputStream(xml.getBytes()); //采用dom4j解析xml字符串.read(new ByteArrayInputStream(xml.getBytes())); Document xmldoc = new XPPReader().read(is); //獲取countrys節點的所有字節點 List countryList = xmldoc.getRootElement().elements(); //定義服務器的響應結果 String result = ""; for(Iterator iterator = countryList.iterator();iterator.hasNext();){ org.dom4j.Element country = (Element)iterator.next(); if(country.getText().equals("1")){ result += "杭州"; }else if(country.getText().equals("2")){ result += "海淀"; }else if(country.getText().equals("3")){ result += "明珠"; } } out.print(result); } }
由於發送到服務器端的是一個xml字符串,因此服務器端不能直接通過request.getParameter();來得到請求參數,而是必須以流的形式來獲取請求參數;
下面將給出一個通用的並且是使用池的技術管理的XMLHttpRequest,因為對於大型的js應用,XMLHttpRequest的使用時很頻繁的,因此使用緩存會更加的高效:
var XMLHttp = { //定義第一個屬性,該屬性用於緩存XMLHttpRequest XMLHttpRequestPool:[], //對象的第一個方法用於返回一個XMLHttpRequest對象 getInstance:function(){ //從XMLHttpRequest對象池中取出一個空閑的XMLHttpRequest對象 for(var i = 0; i< XMLHttpRequestPool.length; i++){ //判斷XMLHttpRequest對象是否為空閑,只需要判斷readyState就可以了,如果readyState為0或4就表示當前XMLHttpRequest對象為空閑 if(this.XMLHttpRequestPool[i].readyState == 0|| this.XMLHttpRequestPool[i].readyState == 4){ return this.XMLHttpRequestPool[i]; } } //如果沒有空閑的就只能再次創建一個新的XMLHttpRequest對象 this.XMLHttpRequestPool[XMLHttpRequestPool.length] = this.createXMLHttpRequest(); //返回剛剛創建的XMLHttpRequest對象 return this.XMLHttpRequestPool[XMLHttpRequestPool.length-1]; }, //創建新的XMLHttpRequest對象 createXMLHttpRequest:function(){ //對於Firefox,Opera等遵守DOM 2規范的瀏覽器 if(window.XMLHttpRequest){ var objXMLHttp = new XMLHttpRequest(); } //對於IE瀏覽器 else{ //將IE瀏覽器不同的XMLHttp實現聲明為數組 var MSXML = ['MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP']; //依次對每個XMLHttp創建XMLHttpRequest對象 for(var i = 0; n< MSXML.length; i++){ try{ //微軟發布的是ActiveX控件 var objXMLHttp = new ActiveXObject(MSXML[i]); //如果正常創建XMLHttpRequest對象就使用break跳出循環 break; }catch(e){ alert("創建XMLHttpRequest對象失敗"); } } } //Mozilla的某些版本沒有readyState屬性 if(objXMLHttp.readyState == null){ //直接設置為0 objXMLHttp.readyState = 0; //對於那些沒有readyState屬性的瀏覽器,將load動作與與下面函數相關聯 objXMLHttp.addEventListener("load", function(){ //當從服務器上加載完數據后,將readyState屬性設為4 objXMLHttp.readyState = 4; if(typeof objXMLHttp.onreadystatechange == "function"){ objXMLHttp.onreadystatechange(); } },false); } return objXMLHttp; }, //定義對象的第三個方法:發送請求(方法[post:get],地址,數據源,回調函數) sendRequest:function(method, url, data, callback){ //得到XMLHttpRequest對象 var objXMLHttp = this.getInstance(); with(objXMLHttp){ try{ //增加一個額外的請求參數,用於防止IE讀取服務器緩存 uri = convertURL(url); //打開與服務器的連接 open(method, uri, true); //對於使用post提交的 if(method == "POST"){ //設定消息請求頭 setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); send(data); } //對於用get方式提交的 if(method == "GET"){ send(null); } //設置狀態改變的回調函數 onreadystatechange = function(){ //當服務器響應結束並得到了正常的服務器響應 if(objXMLHttp.readyState == 4&& objXMLHttp.status == 200){ //調用回調函數 callback(objXMLHttp); } } }catch(e){ alert(e) } } }, convertURL:function (url){ //獲取時間戳 var timstamp=(new Date()).valueOf(); //將時間戳信息拼接到URL上 if(url.indexOf("?")>=0){//用indexof判斷該URL地址是否有問號 url=url+"&t="+timstamp; }else{ url=url+"?t="+timstamp; } return url; } }
上面的池的實現就是簡單的使用一個數組來存儲已存在的XMLHttpRequest對象,這樣這個數組就成了一個XMLHttpRequest對象池,實現緩存的作用,每次發送請求只要從對象池中取出一個閑置的XMLHttpRequest對象,如果此時不存在閑置的對象就創建一個新的XMLHttpRequest對象
以后我們在使用Ajax的時候只需要將這個js代碼進行引入,然后直接調用方法XMLHttp.sendRequest("POST/GET", url, data, callback);
這樣是不是有點像jquery對於get方式提交的封裝:$.get("AjaxServer?name="+userName,null,callback);
具體實例可以見:http://www.cnblogs.com/shenliang123/archive/2012/04/19/2456735.html