该帖已经被评为新手帖
|
|
---|---|
作者 | 正文 |
发表时间:2010-02-05
最后修改:2010-03-07
现在JavaWeb领域,MVC框架越来越多,比较出名的有Struts、Struts2、SpringMVC、WebWork等。而Ajax,作为一种与特定的动态Web编程语言(如Java、C#、PHP)无关的技术,也已经被引入到了Java MVC框架的各家各户。而这些MVC框架,归根到底,都是对Servlet技术的封装。同时,支持Ajax的JavaScript框架(or类库)也越来越多,出名的如Jquery、Ext、Prototype、DWR等,而它们实现异步传输功能还是离不开JavaScript中的XMLHttpRequest这个对象。好,转入正题吧。 Ajax通过XMLHttpRequest对象实现异步传输,那我们首先要获取这个对象。由于浏览器的差异,为了兼容各种常用的浏览器,先写一个初始化XMLHttpRequest对象的方法,代码如下:
对上面的代码,在这里解析一下:XMLHttpRequest对象的请求状态(readyState)有0、1、2、3、4,其中,0表示未初始化,1表示open方法成功调用,2表示服务器应答客户端请求,3表示交互中,HTTP头信息已经收到,但响应数据还没有接收,4表示数据接收完成。我们通过“xmlHttp.onreadystatechange = callback;” 来设置如果XMLHttpRequest对象的请求状态发生改变了,则会执行回调函数callback。我们可以看到,在callback方法体中,我们只关心readyState==4(交互完成)的情况,再获取从服务器端返回的状态码status,常见的状态码有:200表示交互成功,404表示页面没找到,500表示服务器处理错误等。接着,通过XMLHttpRequest的responseText属性得到从服务器端返回的文本数据,或者通过responseXML属性获得XML格式的数据。 在上面的代码中,doGet方法和doPost方法都有参数”data”,它由XMLHttpRequest负责从客户端传送到服务器端,对于Get方法,附在URL尾部,例如:member.jsp?name=xxx&sex=male。对于Post方式,可调用XMLHttpRequest的send方法发送。data的数据形式比较灵活,可以是普通的参数格式、XML格式,JSON格式或者是其他格式,只要你能发送过去,服务器端就有办法将你解析出来。在这里,我们降低难度,就用最简单的参数格式,即 key1=value1 & key2=value2 & key3=value3 & …… 我们都知道,HTTP协议的Get方式传输数据,是通过把这些key-value串附到URL后面的,也就是我们只要点表单的提交按钮,就可以看到地址栏后面会多了一串key-value,代表表单里各输入框的名和值。然后,我们要做异步发送数据,就不能用表单的自动提交了,也就是说,得自己一个一个获取到各输入框的数据,然后再一个一个拼成上面的key-value串再发送。有没有一种简单的办法来组织这些数据呢?大家看到key-value是否会想到Java中的什么类?请看下面代码,我用JavaScript写了一个Map类(JavaScript中的“function”可以看作是方法,也可以看作是面向对象的“类”),就是类似于Java中我们常用的Map接口。
/** * Map类 * 实现了类似于Java语言中的Map接口的常用方法 */ function Map(){ //key集 this.keys = new Array(); //value集 this.values = new Array(); //添加key-value进Map this.put = function(key, value){ if(key == null || key == undefined) return; var length = this.size(); for(var i = 0 ; i < length ; i ++ ) { //如果keys数组中有相同的记录,则不覆盖原记录的值 if(this.keys[i] == key) this.values[i] = value; } this.keys.push(key); this.values.push(value); }; //获取指定key的value this.get = function(key){ var length = this.size(); for(var i = 0 ; i < length ; i ++ ) { if(this.keys[i] == key) { return this.values[i]; } else { continue; } return null; } }; //移除指定key所对应的map this.remove = function(key) { var length = this.size(); for(var i = 0 ; i < length ; i ++ ) { if(this.keys[i] == key) { while(i < length - 1) { this.keys[i] = this.keys[i+1]; this.values[i] = this.values[i+1]; i ++ ; } //处理最后一个元素 this.keys.pop(); this.values.pop(); break; } } }; //是否包含指定的key this.containsKey = function(key) { var length = this.size(); for(var i = 0 ; i < length ; i ++ ) { if(this.keys[i] == key) { return true; } } return false; }; //是否包含指定的value this.containsValue = function(value) { var length = this.size(); for(var i = 0 ; i < length ; i ++ ) { if(this.values[i] == value) { return true; } } return false; }; //包含记录总数 this.size = function() { return this.keys.length; }; //是否为空 this.isEmpty = function() { return this.size() == 0 ? true : false; }; //清空Map this.clear = function() { this.keys = new Array(); this.values = new Array(); }; //将map转成字符串,格式:key1=value1,key2=value2 this.toString = function() { var length = this.size(); var str = ""; for(var i = 0 ; i < length ; i ++ ) { str = str + this.keys[i] + "=" + this.values[i]; if(i != length-1) str += ","; } return str; }; }
代码比较长,有些方法在本例中可能用不到,但也写出来了,或者在其他地方可能有用吧。当我们使用这个Map类来存储HTTP的参数时,发觉有几个不妥的地方:一是put方法,在Java的Map接口中,是不允许有重复的key存在的,而在JavaScript中作为传输参数的载体时,很多时候会出现多个同名的key的,例如处理表单的checkbox时,同一个name的有几个checkbox,构成一个复选框组,组织参数时就形如“key=value1&key=value2”,故put方法必须改。也是由于这个原因,get方法和remove方法也要改。二是toString方法,key=value对,不是用“,”号隔开的,而是用“&”号,故toString方法也须改。而有时候想想,如果把Map类改了,如果其他地方要用到的话,是不是还是改回来,与其改来改去的,不如继承它,重写put 、get、remove和toString方法。好主意,代码如下: /** * ParamMap类,用于存储HTTP请求中的Get方法或者Post方法所传递的参数 * 继承于Map类,但改写一些方法,以适合HTTP请求中的参数格式 * 与Map不同之处有:ParamMap允许多个同名的"key"存在, * toString方法返回的"key=value"对以"&"号连接,而不是","号,等等。 */ function ParamMap() { //继承Map类 Map.call(this); //重写put方法,允许多个同名key存在 this.put = function(key, value){ if(key == null || key == undefined) return; this.keys.push(key); this.values.push(value); }; //重写get方法,返回values数组 this.get = function(key) { var results = new Array(); var length = this.size(); for(var i = 0 ; i < length ; i ++ ) { if(this.keys[i] == key) results.push(this.values[i]); } return results; }; //重写remove方法 this.remove = function(key) { var length = this.size(); for(var i = 0 ; i < length ; i ++ ) { if(this.keys[i] == key) { while(i < length - 1) { this.keys[i] = this.keys[i+1]; this.values[i] = this.values[i+1]; i ++ ; } //处理最后一个元素 this.keys.pop(); this.values.pop(); } } }; //重写toString方法, 转成XMLHttpRequest.send(ajaxString)方法的参数格式的字符串, //形如:key1=value1&key2=value2 this.toString = function() { var length = this.size(); var str = ""; for(var i = 0 ; i < length ; i ++ ) { str = str + this.keys[i] + "=" + this.values[i]; if(i != length-1) str += "&"; } return str; }; } 怎么使用这个ParamMap类呢,且看下面的示例代码:
在JavaScript中,用来获取HTML结点的方法,常用的有如下方法: Node document.getElementById("username") //根据标签的id NodeList document.getElementsByName(“username”) //根据标签的name NodeList document.getElementsByTagName("input") //根据标签的标签名 我们注意到在除了getElementById是返回Node对象外,其他两个方法都是返回NodeList对象,相当于Node数组。在Ajax应用中,根据ID来获取节点,很多时候十分方便,如获取text、password、hidden、textarea类型的值,但有时候并不那么方便,如处理checkbox、radio(不允许多个同名的id)。况且有许多情况下,开发者是由“非Ajax”转成Ajax应用的。在还没有引进Ajax的时候,表单传值都是根据输入域的name的值来区分的,不管是Get方式还是Post方式,在服务器端(这里指Java Servlet)获取数据的代码如下:
所谓的“单值表单域”就是上面提到过的type为text、password、hidden的input或者textarea等,而“多值表单域”是指checkbox。其实,也不尽然,如单值表单域有时候也可以是多值表单域,如我们注册时有时会要求填多个邮箱,这个<input type=”text” name=”email”>就可以重复多行用同一个name,它在Servlet端获取值方式跟checkbox一样处理。 鉴于各种原因,我们在JavaScript获取HTML Form表单域方法,决定采用getElementsByName方法,这样,我们是不是每取一个表单元素,就得将NodeList类型的返回结果遍历一遍,获取Node,再从Node获取值呢?既然选择了这样做,当然少不了这样的工作,不过,我们可以写一个可重用的方法,让它处理一下NodeList对象。且看代码:
上面代码将NodeList转成了值数组,而且让使用者忽略表单域类型的差异,专心于值的获取和使用。美中不足的是,上面的方法返回的是数组,我们还是一个一个遍历再拼成key=value字符串。接下来的方法不但解决了这一点,而且还大大简化我们的表单取值,且看:
这个方法返回值的类型是ParamMap,也就是我们刚才定义的用来传递参数的类,这个类中有一个toString的方法,不就是返回“key1=value1&key2=value2”形式的字符串吗? OK,下面的测试例子,将涉及到表单的各种元素域,其中引进了ajax.js文件,其内容即是我们上面所写的JS函数和类,在附件中可下载到,在此就不重复贴出来了。且看HTML代码:
<?xml version="1.0" encoding="UTF-8" ?> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>测试Ajax与Servlet之间的数据传递</title> <script type="text/javascript" src="js/ajax.js"></script> <script type="text/javascript"> window.onload = function(){ var submit = document.getElementById("submit"); submit.onclick = function(){ var map = parseForm2ParamMap("username", "email", "password", "userid", "sex", "framework", "salary", "dream"); //doPost("input", map.toString(), callback); //默认Alert出信息 doPost("input", map.toString(), function(){ if(xmlHttp.readyState == 4) { if(xmlHttp.status == 200) { var returnMsg = document.getElementById("returnMsg"); returnMsg.innerHTML = xmlHttp.responseText; } } }); }; }; </script> </head> <body> <h2>测试Ajax与Servlet之间的数据传递<br />(Step1:从Client->Server)</h2> <div id="returnMsg" style="color:red;"></div> <form> <!-- 文本域 --> <p>你的姓名: <input type="text" name="username" /><br /> </p> <p>你的电子邮件(可填两个):<br /> 邮件地址1:<input type="text" name="email" /> <br /> 邮件地址2:<input type="text" name="email" /> </p> <!-- 密码域 --> <p>你的密码: <input type="password" name="password" /><br /> </p> <!-- 隐藏域 --> <input type="hidden" name="userid" value="108" /><br /> <!-- 单选框 --> <p>你的性别 : <label><input type="radio" name="sex" value="male" />男</label> <label><input type="radio" name="sex" value="female" />女</label> </p> <!-- 复选框 --> <p>你掌握的开源框架 :<br /> <label><input type="checkbox" name="framework" value="struts" />Struts</label> <label><input type="checkbox" name="framework" value="spring" />Spring</label> <label><input type="checkbox" name="framework" value="hibernate" />Hibernate</label> <label><input type="checkbox" name="framework" value="ibatis" />iBatis</label> <label><input type="checkbox" name="framework" value="webwork" />WebWork</label> </p> <!-- 下拉列表 --> <p>你的薪水范围 : <select name="salary"> <option value="2000以下">2000以下</option> <option value="2000——3000">2000——3000</option> <option value="3000——4000">3000——4000</option> <option value="4000——5000">4000——5000</option> <option value="5000以上">5000以上</option> </select> </p> <!-- 文本域 --> <p>你的职业梦想: <br /> <textarea rows="5" cols="36" name="dream"></textarea><br /> </p> <input type="button" id="submit" value="submit" /> </form> </body> </html>
服务器端的代码如下: package simple.rongxh.servlet; 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; /** * RongXinHua心梦帆影 * site1: http://rongxh2010.iteye.com * site2: http://www.blogjava.net/rongxh7 * @author rongxinhua * */ public class Input extends HttpServlet { private static final long serialVersionUID = 1L; private void process(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获取数据 request.setCharacterEncoding("UTF-8"); String username = request.getParameter("username"); String[] emails = request.getParameterValues("email"); String password = request.getParameter("password"); String userid = request.getParameter("userid"); String sex = request.getParameter("sex"); String[] framework = request.getParameterValues("framework"); String salary = request.getParameter("salary"); String dream = request.getParameter("dream"); //响应给客户端 response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.append("恭喜你,保存成功,你的基本信息如下:<br/>"); out.append("姓名:" + username + "<br/>"); for(int i = 0 ; i < emails.length ; i ++ ) { out.append("邮箱" + (i+1) + ":" + emails[i] + "<br/>"); } out.append("密码:" + password + "<br/>"); out.append("编号:" + userid + "<br/>"); out.append("性别:" + sex + "<br/>"); out.append("框架:"); for(int i = 0 ; framework != null && i < framework.length ; i ++ ){ out.append(framework[i] + " "); } out.append("<br/>"); out.append("薪水:" + salary + "<br/>"); out.append("梦想:" + dream + "<br/>"); out.println(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { process(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { process(request, response); } }
运行结果截图如下:
当Java遇上Ajax,本文只是第一篇,欢迎提出宝贵的意见和建议,我将十分感谢!
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-02-07
对于学习Ajax很有帮助阿!
|
|
返回顶楼 | |
发表时间:2010-02-07
var map = new ParamMap(); map.put("username", username); map.put("password", password); map.put("sex", sex); doGet("test/register", map.toString(), callback); doPost("test/register", map.toString(), callback); 直接用json不是更好? |
|
返回顶楼 | |
发表时间:2010-02-07
最后修改:2010-02-07
jee与ajax有啥关系嘛,纯属YY贴,ajax是浏览器端,根本与java没关系,jq,ext 是ajax框架??????????,摆脱多学学在发帖吧,别误导了初学者!!
|
|
返回顶楼 | |
发表时间:2010-02-07
kjj 写道 jee与ajax有啥关系嘛,纯属YY贴,ajax是浏览器端,根本与java没关系,jq,ext 是ajax框架??????????,摆脱多学学在发帖吧,别误导了初学者!!
照楼上所说的jq,ext该是什么框架呢 |
|
返回顶楼 | |
发表时间:2010-02-07
kjj 写道 jee与ajax有啥关系嘛,纯属YY贴,ajax是浏览器端,根本与java没关系,jq,ext 是ajax框架??????????,摆脱多学学在发帖吧,别误导了初学者!!
jq,ext 不是ajax框架??????????,摆脱多学学在发帖吧,别误导了初学者!! |
|
返回顶楼 | |
发表时间:2010-02-07
引用 //如果keys数组中有相同的记录,则不保存重复记录 if(this.keys[i] == key) return; map不是这样的吧.... |
|
返回顶楼 | |
发表时间:2010-02-08
不错,对初学的还是挺有帮助的
beck5859509 写道 kjj 写道 jee与ajax有啥关系嘛,纯属YY贴,ajax是浏览器端,根本与java没关系,jq,ext 是ajax框架??????????,摆脱多学学在发帖吧,别误导了初学者!!
照楼上所说的jq,ext该是什么框架呢 准确点说:jQuery、Mootools、ext等是 JS 框架 |
|
返回顶楼 | |
发表时间:2010-02-08
最后修改:2010-02-08
源码测试提示error,activeXName少了s
for(var i = 0 ; i < activeXName.length ; i ++ ) { 测试Ajax与Servlet之间的数据传递(rongxh2010)在线效果可看 http://www.sh2999.com/sh/ajax/testAjaxSubmit.jsp |
|
返回顶楼 | |
发表时间:2010-02-08
引用 jq,ext 是ajax框架...
确实有这种说法,它们也确实都封装了ajax的操作。知道它们能用来干吗就行了。 |
|
返回顶楼 | |