剖析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

 

 

 

 

 

posted on 2012-05-13 21:50  发表是最好的记忆  阅读(16771)  评论(3编辑  收藏  举报