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

  • 相关阅读:
    牛客网 二叉树的镜像 JAVA
    牛客网 反转链表 JAVA
    牛客网 调整数组顺序使奇数位于偶数前面 JAVA
    Integer to Roman LeetCode Java
    Valid Number leetcode java
    Longest Common Prefix
    Wildcard Matching leetcode java
    Regular Expression Matching
    Longest Palindromic Substring
    Add Binary LeetCode Java
  • 原文地址:https://www.cnblogs.com/shenliang123/p/2498524.html
Copyright © 2020-2023  润新知