JavaWeb(十三)AJAX&&XStream&&JSON

目录

AJAX

    概念

    工作原理

    使用套路

创建XMLHttpRequest

指定请求头

发送请求

接收服务器响应

示范用例

    案例

处理POST请求(注册)

返回XML

省市联动

XStream

JSON

Ajax小工具

后话


AJAX

    概念

  AJAX全称Asynchronous JavaScript And XML,译为“异步JavaScript和XML”。即通过JavaScript与服务器进行异步交互,对网页的某部分进行更新。

  那么何为异步交互呢?异步交互即当发送一个请求时,无需等待服务器的响应即可发第二个请求。通过这样的现象可以使用js接收服务器的响应,然后利用js来局部刷新页面中的某一部分内容。而同步交互则是必须得等待第一个请求响应结束后,才能发出第二个请求。


    工作原理

  上一个图,源自RUNOOB。

  需要关注的是第一个方框中的XMLHttpRequest对象,这个对象是Ajax核心,用于在后台与服务器交换数据。可以说掌握了它就掌握了Ajax。创建XMLHttpRequest对象后,随后用其打开与服务器的连接,将请求发送至服务器。服务器处理完请求后将响应返回浏览器后,XMLHttpRequest对象就会获取到响应来完成操作。


    使用套路

创建XMLHttpRequest

    在创建之前,需要知道现代浏览器(IE7+、Chrome、FireFox、Safari以及Opera)都支持XMLHttpRequest对象,只有IE5、IE6使用ActiveX对象。为了对所有浏览器支持,需要检查浏览器是否支持XMLHttpRequest对象,即代码如下所示:

function createXMLHttpRequest() {
    if(window.XMLHttpRequest) {
        return new XMLHttpRequest();
    } else {
        return new ActiveXObject("Microsoft.XMLHTTP");
    }
}

指定请求头

    查看API,在XMLHttpRequest中有一个方法用于指定请求的内容,如下所示:

        open(method, url, async)

    其中,method表示请求方式,如GET、POST、DELETE请求等;url表示要访问的位置,即指定服务器端资源;async表示是否异步处理请求,当为true时为异步处理请求,为false时为同步处理请求。

发送请求

    当设置完请求内容后,需要将请求发送至服务器,而在XMLHttpRequest中有很多方法来处理不同数据形式,如下所示:

        send(data : Document)
        send(data : FormData)
        send(data : Blob)
        send(data : ArrayBuffer)
        send(data : String)

    方法中的参数其实就是请求体的内容,当请求方式为get时需要设置参数为null,以防部分浏览器无法发送。

接收服务器响应

    当请求发送至服务器端后,服务器端将请求处理完毕把响应发送给浏览器。在XMLHttpRequest中有一个onreadystatechange事件,它会在XMLHttpRequest对象的状态发生变化时被调用。在说这个事件之前,需要知道XMLHttpRequest中有五个状态,如下所示:

        0:初始化未完成状态,即创建了对象但未调用open()方法
        1:请求已开始状态,即调用了open()方法但未调用send()方法
        2:请求发送完成状态,即调用了send()方法
        3:开始读取服务器响应
        4:读取服务器响应结束

    那么到底如何得到XMLHttpRequest对象的状态、服务器响应的状态码以及响应的内容的?这些在XMLHttpRequest对象中都有相对应的实例属性来处理,即如下所示:

    XMLHttpRequest xmlHttp = new XMLHttpRequest();
    // 得到XMLHttpRequest对象的状态
    var state = xmlHttp.readyState;
    // 得到服务器响应的状态码
    var status = xmlHttp.status;
    // 得到服务器响应的内容
    var content = xmlHttp.responseText;
    var content = xmlHttp.responseXML;

    需要注意的是,responseText和responseXML得到的内容是不同类的,前者是字符串String,后者是Document对象。

示范用例

    根据这四个套路,先创建一个页面,如下所示:

<head>
<script type="text/javascript">
    function createXMLHttpRequest() {
        if (window.XMLHttpRequest) {
            return new XMLHttpRequest();
        } else {
            return new ActiveXObject("Microsoft.XMLHTTP");
        }
    }

    window.onload = function() {
        var btn = document.getElementById("btn");
        btn.onclick = function() {
            // 1.得到异步对象
            var xmlHttp = createXMLHttpRequest();
            // 2.设置请求头
            xmlHttp.open("GET", "<c:url value='/AjaxServlet1'/>", true);
            // 3.发送请求
            xmlHttp.send(null);
            // 4.注册监听事件获得响应
            xmlHttp.onreadystatechange = function() {
                if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
                    // 获取服务器的响应内容
                    var text = xmlHttp.responseText;
                    var context = document.getElementById("content");
                    content.innerHTML = text;
                }
            };
        };
    };
</script>

</head>
<body>
    <button id="btn">click here</button>
    <h2 id="content"></h2>
</body>

    接着写一个Servlet

public class AjaxServlet1 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        response.getWriter().print("Hello Ajax!");
    }
}

    结果如下所示:

    需要特别说明的一点:JS中是可以使用Jstl标签库的,原因是在于页面由服务器发送,在发送之前会将jsp进行编译,而产生的html格式页面则将JS其中的标签变成了普通字符串。


    案例

处理POST请求(注册)

    以注册为例,表单需要对输入的用户名进行验证,以防同名用户出现。在XMLHttpRequest中的send()方法可以传表单项参数亦可传递整个表单的表单项集合。为了达到传递整个表单的表单项集合,决定使用FormData对象。

    FormData对象是将表单项的key以及value形成键值对以序列化的形式用于方便传递至服务器。而使用了FormData对象等同于setRequestHeader("Content-Type", "multipart/form-data"),即常规的request.getParameter(String name)方法不可再使用,这里就得使用回FileUpload包。

    明确了这两点后,先创建一个页面,其如下所示:

    <form id='test-form' method="POST" >
        用户名: <input type="text" name='username' id="usernameEle"><span id="errorSpan"></span><br>
        密码: <input type="text" name='password'><br>
        <input type='submit' value="注册">
    </form>
    <h2 id="content"></h2>

    获取表单元素对其提交事件进行注册,如下所示:

window.onload = function() {
        var form = document.getElementById('test-form');
        form.onsubmit = function(event) {
            event.preventDefault();
			
            // 1.得到异步对象
            var xmlHttp = createXMLHttpRequest();
            // 2.设置请求头
            xmlHttp.open("POST", "<c:url value='/AjaxServlet1'/>", true);
            // 3.发送请求
            var formData = new FormData(form);
            xmlHttp.send(formData);
            // 4.注册监听事件获得响应
            xmlHttp.onreadystatechange = function() {
                if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
                    // 获取服务器的响应内容
                    var text = xmlHttp.responseText;
                    var context = document.getElementById("content");
                    content.innerHTML = text;
                }
            };
        };

        // ...其他代码...
}

    Servlet获取表单请求参数,如下所示:

public class AjaxServlet1 extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");

        String str = "";
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload sfu = new ServletFileUpload(factory);
        try {
            List<FileItem> fileItems = sfu.parseRequest(request);
            for(FileItem fileItem : fileItems) {
                if (fileItem.isFormField()) {
                    str += fileItem.getString("UTF-8") + " ";
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        }
        response.getWriter().print("Hello Ajax!" + str);
    }
}

    接下来简单演示用户名的表单项验证。当用户在输入框中用户名时向服务器端请求查询,当用户名等于“张三”时返回“1”,反之则返回“0”。接着通过异步对象获取服务器的响应内容,当响应内容为“1”时显示红色的“用户名已注册”的提示,为“0”时显示绿色的“用户名可使用”的提示。

    因此创建一个名为ValidateUsernameServlet,代码如下所示:

public class ValidateUsernameServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) {
        request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");

        if (request.getParameter("username").equals("张三")) {
            response.getWriter().print("1");
        } else {
            response.getWriter().print("0");
        }
    }
}

    在window.onload里加入以下代码:

        var userEle = document.getElementById("usernameEle");
        userEle.onblur = function () {
            // 1.得到异步对象
            var xmlHttp = createXMLHttpRequest();
            // 2.设置请求头
            xmlHttp.open("POST", "<c:url value='/ValidateUsernameServlet'/>", true);
            xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            // 3.发送请求
            xmlHttp.send("username=" + userEle.value);
            // 4.注册监听事件获得响应
            xmlHttp.onreadystatechange = function() {
                if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
                    // 获取服务器的响应内容
                    var text = xmlHttp.responseText;
                    var errorSpan = document.getElementById("errorSpan");
                    if (text == "1") {
                        errorSpan.style.color = 'red';
                        errorSpan.innerHTML = "用户名已被注册";
                    } else {
                        errorSpan.style.color = 'green';
                        errorSpan.innerHTML = "用户名可使用";
                    }
                }
            };
        };

    显示结果如下:

返回XML

    上面说过,XMLHttpRequest可以返回两种响应内容,其中一种是文本,另一种是XML。写一个Servlet如下所示(为了方便演示直接在Servlet中定义了一个xml样板):

public class AjaxServlet2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        String xml = "<users>" + 
                "<user id = '0001'>" +
                "<username>zhangsan</username>" + 
                "<password>123456</password>" + 
                "</user>" + 
                "</users>";
		
        response.setContentType("text/xml;charset=utf-8");
        response.getWriter().print(xml);
    }
}

    页面的JS代码如下所示:

    window.onload = function() {
        var btn = document.getElementById("btn");
        btn.onclick = function() {
            // 1.得到异步对象
            var xmlHttp = createXMLHttpRequest();
            // 2.设置请求头
            xmlHttp.open("GET", "<c:url value='/AjaxServlet2'/>", true);
            // 3.发送请求
            xmlHttp.send(null);
            // 4.注册监听事件获得响应
            xmlHttp.onreadystatechange = function() {
                if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
                    // 获取服务器的响应内容
                    var doc = xmlHttp.responseXML;
                    var ele = doc.getElementsByTagName('user')[0];
                    var id = ele.getAttribute('id');
                    var username;
                    if(window.addEventListener) {
                        username = doc.getElementsByTagName('username')[0].textContent;
                    } else {
                        username = doc.getElementsByTagName('username')[0].text;// IE
                    }
                    var password;
                    if(window.addEventListener) {
                        password = doc.getElementsByTagName('password')[0].textContent;
                    } else {
                        password = doc.getElementsByTagName('password')[0].text;// IE
                    }
                    
                    var text = id + "," + username + "," + password;
                    document.getElementById('content').innerHTML = text;
                }
            };
        };
    };

    结果如下所示:

省市联动

    所谓的“省市联动”即两个下拉框,通过选择省份的下拉框,动态显示市级的选项。页面如下所示:

    <select name="province" id="province">
        <option>===请选择省===</option>
    </select>
	     
    <select name="city" id="city">
        <option>===请选择市===</option>
    </select>

    在这里用xml文件来演示,即如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<china>
    <province name="广东">
        <city>广州</city>
        <city>深圳</city>
        <city>韶关</city>
        <city>珠海</city>
        <city>佛山</city>
        <city>湛江</city>
    </province>
    <province name="四川">
        <city>成都</city>
        <city>自贡</city>
        <city>攀枝花</city>
        <city>德阳</city>
        <city>乐山市</city>
    </province>
</china>

    需要实现的效果:当页面加载完毕后,向服务器端发送请求,得到所有省份的名称,显示在下拉框中。Servlet代码如下所示:

public class ProvinceServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        try {
            // 解析XML得到Document对象
            SAXReader reader = new SAXReader();
            InputStream input = this.getClass().getResourceAsStream("/china.xml");
            Document doc = reader.read(input);
			
            /*
             * 查询所有province的name属性,得到一堆的属性对象
             * 循环遍历,把所有的属性值连接成一个字符串,发送给客户端
             * */
            List<Attribute> attrList = doc.selectNodes("//province/@name");
            StringBuilder sb = new StringBuilder();
            for(int i = 0; i <attrList.size(); i++) {
                sb.append(attrList.get(i).getValue());
                if (i < attrList.size() - 1)
                    sb.append(",");
            }
            response.getWriter().print(sb);
			
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

    在window.onload添加以下代码:

window.onload = function() {
        var xmlHttp = createXMLHttpRequest();
        xmlHttp.open("GET", "<c:url value='/ProvinceServlet' />", true);
        xmlHttp.send();
        xmlHttp.onreadystatechange = function () {
            if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
                var text = xmlHttp.responseText;
                var arr = text.split(",");
                for(var i = 0; i < arr.length; i++) {
                    var option = document.createElement("option");
                    option.value = arr[i];
                    var textNode = document.createTextNode(arr[i]);
                    option.appendChild(textNode);
					
                    document.getElementById("province").appendChild(option);
                }
            }
        };
};

    访问页面,可以发现在省份的下拉列表中已经可以看到两个省。接下来完成当选取其中一个省份时,在市的下拉框显示对应的所属省份的城市。需要用到的Servlet代码如下所示:

public class CityServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) {
        try {
            request.setCharacterEncoding("UTF-8");
            // 需要注意返回的是xml
            response.setContentType("text/xml;charset=utf-8");
			
            SAXReader reader = new SAXReader();
            InputStream input = this.getClass().getResourceAsStream("/china.xml");
            Document document = reader.read(input);
			
            String pname = request.getParameter("pname");
            Element provinceEle = (Element) document.selectSingleNode("//province[@name='" + pname + "']");
            String xmlStr = provinceEle.asXML();
            response.getWriter().print(xmlStr);
			
        } catch (Exception e) {
            throw new RuntimeException(e);
    }
}

    向省份的下拉列表添加改变监听,如下所示:

        var proSelect = document.getElementById("province");
        proSelect.onchange = function () {
            var xmlHttp = createXMLHttpRequest();
            xmlHttp.open("POST", "<c:url value='/CityServlet' />", true);
            xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xmlHttp.send("pname=" + proSelect.value);
            xmlHttp.onreadystatechange = function () {
                var citySelect = document.getElementById("city");
                var optionList = citySelect.getElementsByTagName("option");
                while (optionList.length > 1)
                    citySelect.removeChild(optionList[1]);
                if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
                    var doc = xmlHttp.responseXML;
                    var cityList = doc.getElementsByTagName("city");
                    for(var i = 0; i < cityList.length; i++) {
                        var cityEle = cityList[i];
                        var cityName = cityEle.innerHTML;
                        var option = document.createElement("option");
                        option.value = cityName;
                        var textNode = document.createTextNode(cityName);
                        option.appendChild(textNode);
						
                        citySelect.appendChild(option);
                    }
                }
            };
        };

    访问页面,结果如下所示:


XStream

Xstream是一个功能比较强大的xml和java对象互转的工具包。在使用XStream之前需要导入两个包:一个是核心包xstream-1.4.7.jar,一个是依赖包xpp3_min-1.1.4c(XML Pull Parser)。

使用方法如下:

        XStream xstream = new XStream();
        String xmlStr = xstream.toXML(javabean);

类中包含的方法如下所示:

    // 别名:即将类型对应的元素名修改成参数name
    public void alias(String name, Class type);
    // 将类的成员生成为元素的属性
    public void useAttributeFor(Class definedIn, String fieldName);
    // 去除集合类型的成员,只保留内容
    public void addImplicitCollection(Class ownerType, String fieldName);
    // 去除类的指定成员的名称,即不生成对应的xml元素
    public void omitField(Class definedIn, String fieldName);

分别创建一个Province以及City类,代码如下所示:

public class Province {
    private String name;
    private List<City> cities = new ArrayList<City>();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<City> getCities() {
        return cities;
    }

    public void setCities(List<City> cities) {
        this.cities = cities;
    }

    public void addCity(City city) {
        cities.add(city);
    }
}

public class City {
    private String name;
    private String description;
	
    public City(String name, String description) {
        super();
        this.name = name;
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

}

接下来开始演示,代码如下所示:

    public List<Province> getProvinceList() {
        Province p1 = new Province();
        p1.setName("广东");
        p1.addCity(new City("深圳", "ShenZhen"));
        p1.addCity(new City("广州", "GuangZhou"));

        Province p2 = new Province();
        p2.setName("四川");
        p2.addCity(new City("成都", "ChengDu"));
        p2.addCity(new City("德阳", "DeYang"));

        List<Province> provinceList = new ArrayList<Province>();
        provinceList.add(p1);
        provinceList.add(p2);

        return provinceList;
    }

    @Test
    public void func3() {
        List<Province> proList = getProvinceList();
        XStream xstream = new XStream();

        xstream.alias("china", List.class);
        xstream.alias("province", Province.class);
        xstream.alias("city", City.class);

        // 把Province类的name属性生成<province>元素的属性
        xstream.useAttributeFor(Province.class, "name");
        // 去除Collection类型的属性,即cities
        xstream.addImplicitCollection(Province.class, "cities");
        // 让City类的description属性不生成对应的xml元素
        xstream.omitField(City.class, "description");

        System.out.println(xstream.toXML(proList));
    }

结果如下所示:

<china>
  <province name="广东">
    <city>
      <name>深圳</name>
    </city>
    <city>
      <name>广州</name>
    </city>
  </province>
  <province name="四川">
    <city>
      <name>成都</name>
    </city>
    <city>
      <name>德阳</name>
    </city>
  </province>
</china>

JSON

JSON是JS提供的一种数据交换格式。所谓的JSON实际上是一个字符串表达形式,在这里稍微提及一下JSON的语法,对象的属性名必须用双引号,属性值可以是为null、数值、字符串、数组、布尔值。

之所以在这里提到JSON,主要是为了说一个方便的小工具json-lib,其可以使用Java语言来创建JSON字符串,亦可把JavaBean转换成JSON。

json-lib小工具所需要的包比XStream的包还多,其中json-lib.jar为核心包。

小工具里使用JSONObject来表示JSON,实际上是一个实现了JSON、Map、Comparable接口的一个类。。

接下来演示一下小工具的使用,演示代码如下所示:

public class Demo01 {

    public void func1() {
        JSONObject map = new JSONObject();
        map.put("name", "zhangsan");
        map.put("age", 23);
        map.put("sex", "male");
		
        String s = map.toString();
        System.out.println(s);
    }
    
    public void func2() {
        Student stu = new Student("zhangsan", 20, "male");
        JSONObject map = JSONObject.fromObject(stu);
        System.out.println(map.toString());
    }

    public void func3() {
        Student stu = new Student("zhangsan", 20, "male");
        Student stu2 = new Student("lisi", 18, "female");
		
        JSONArray list = new JSONArray();
        list.add(stu);
        list.add(stu2);
		
        System.out.println(list.toString());
    }
    @Test
    public void func4() {
        Student stu = new Student("zhangsan", 20, "male");
        Student stu2 = new Student("lisi", 18, "female");
        List<Student> list = new ArrayList<Student>();
        list.add(stu);
        list.add(stu2);
		
        System.out.println(JSONArray.fromObject(list).toString());
    }
	
}

所用的JavaBean对象如下所示:

public class Student {
    private String name;
    private int age;
    private String sex;

    public Student(String name, int age, String sex) {
        super();
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

对于这个小工具只要会使用即可,若是想研究其内部机理,可以查看API以及源代码。

Ajax小工具

在先前学案例的时候会发现许多代码都是重复的,将重复代码部分整理,得到如下工具:

function createXMLHttpRequest() {
    if (window.XMLHttpRequest) {
        return new XMLHttpRequest();
    } else {
        return new ActiveXObject("Microsoft.XMLHTTP");
    }
}

// method/*请求方式*/, 
// url/*请求的url*/, 
// asyn/*是否异步*/, 
// params/*请求体*/, 
// callback/*回调方法*/, 
// type/*服务器响应数据转换成什么类型*/
function ajax(option) {
    // 得到xmlHttp
    var xmlHttp = createXMLHttpRequest();
    if(!option.method)
        option.method = "GET";
    if(option.asyn == undefined)
        option.asyn = true;
    // 打开连接
    xmlHttp.open(option.method, option.url, option.asyn);
    if (option.method == "POST") {
        xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    }
    // 发送请求
    xmlHttp.send(option.params);
    // 注册监听
    xmlHttp.onreadystatechange = function () {
        if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
            var data;
            if (!option.type) {
                data = xmlHttp.responseText;
            } else if (option.type == "xml") {
                data = xmlHttp.responseXML;
            } else if (option.type == "text") {
                data = xmlHttp.responseText;
            } else if (option.type == "json") {
                var text = xmlHttp.responseText;
                data = eval("(" + text + ")");
            }
			
            callback(data);
        }
    }
}

后话

    '''
        若是未学习过JS以及JQuery,这一个小工具可以方便理解JQuery中的$.ajax()的构造
    '''

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值