剖析XMLHttpRequest


學過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

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM