zoukankan      html  css  js  c++  java
  • 剖析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

  • 相关阅读:
    vsync信号产生与分发
    推荐看过不错的博客及网站
    证明质数有无数个
    242 Valid Anagram
    169 Majority Element
    快速排序--quicksort
    插入排序
    选择排序
    冒泡排序
    指针函数 函数指针 回调函数
  • 原文地址:https://www.cnblogs.com/shenliang123/p/2498524.html
Copyright © 2011-2022 走看看