Struts 2与AJAX(第三部分)
http://www.blogjava.net/max/archive/2007/08/16/137363.html很久没有更新BLOG了,前一段时间公司的项目比较忙,另外我还和一位出版社的朋友谈写书的事情,所以一直没有时间,完成《Struts2与AJAX》。后来写书的事情吹了,趁今天有点空闲就把它完成。
在大家看这部分文章之前,我想对于写书的事情说两句,或者应该叫发牢骚才对。通过这次写书失败的经历,我明白为什么国内的IT书籍多数是滥于充数、粗制滥造、缺乏经典。其实说白了就是一个“钱”字作怪。为了市场,很多编辑可能会“建议”你去“抄考”一些国内相对畅销的同类书籍,例如写Struts就一定要按所谓的MVC进行目录分类,美其名曰“容易入门”。我认为“MVC”的概念虽然重要,但对初学者而言,需要对编程有一定的了解才容易明白此概念。另外,为了“实用”,不惜使用相同的技术重复编写不同的范例。可能是我不太了解读者的心理吧。
言归正传,在上两部分的《Struts2与AJAX》中我介绍了Struts2与DOJO结合实现AJAX的知识,本文将介绍在Struts2中使用DWR实现AJAX表单校验。
什么是DWR
DWR(DirectWebRemoting)是在JavaEE中较流行的AJAX框架,它的最大优势就是可以像使用本地的Javascript函数一样,调用服务器上的Java方法。如下图所示:
图1DWR工作原理
其实DWR原理也不复杂,它先在web.xml中配置一个Servlet,映射到特定的路径(通常是%CONTEXT_PATH%/dwr/*)。这个Servlet的作用就是初始化要暴露给Javascript调用的Java类(通过dwr.xml进行配置),并生成相应的代理的Javascript类代码。在XHR请求到来的时候,Servlet负责将请求的参数变成对应的Java对象,并以其为参数调用目标Java方法,并将返回值转化为Javascript代码。详情请参考:http://getahead.ltd.uk/dwr/
Struts2与DWR
在Struts2.0.x中使用DWR实现AJAX表单校验。在大家掌握了DWR的原理后,下面我想详细介绍一下实现的步骤。
首先,到以下站点https://dwr.dev.java.net/files/documents/2427/47455/dwr.jar下载DWR的1.1.4版本的JAR包。需要注意的是,DWR虽然已经发布2.0版本,但它与1.1.4有很大的区别,所以请大家不要使用2.0版本,否则会出现异常的;
接着,新建WEB工程,将下图所示的JAR包加入到工程的“BuildPath”中;
图2依赖的JAR包
接下来,配置web.xml文件,内容如下:
<?xmlversion="1.0"encoding="UTF-8"?>
<web-appid="WebApp_9"version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2eehttp://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Struts2AJAXPart3</display-name>
<filter>
<filter-name>struts-cleanup</filter-name>
<filter-class>
org.apache.struts2.dispatcher.ActionContextCleanUp
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts-cleanup</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--开始DWR配置-->
<servlet>
<servlet-name>dwr</servlet-name>
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
<!--结束DWR配置-->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>清单1WebContent/WEB-INF/web.xml
然后是DWR的配置文件:
<?xmlversion="1.0"encoding="UTF-8"?>
<!--STARTSNIPPET:dwr-->
<!DOCTYPEdwrPUBLIC
"-//GetAheadLimited//DTDDirectWebRemoting1.0//EN"
"http://www.getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
<allow>
<createcreator="new"javascript="validator">
<paramname="class"value="org.apache.struts2.validators.DWRValidator"/>
</create>
<convertconverter="bean"match="com.opensymphony.xwork2.ValidationAwareSupport"/>
</allow>
<signatures>
<![CDATA[
importjava.util.Map;
importorg.apache.struts2.validators.DWRValidator;
DWRValidator.doPost(String,String,Map<String,String>);
]]>
</signatures>
</dwr>
<!--ENDSNIPPET:dwr-->清单2WebContent/WEB-INF/dwr.xml
通过以上配置,我们可以将DWRValidator中的方法暴露为Javascript可以调用的远程接口。
在正确完成以上步骤之后,我们发布运行一下应用程序,在浏览器地址栏中输入http://localhost:8080/Struts2_Ajax3/dwr/,应该会出现如下页面:
图3DWRServlet默认输出页面
接下来,我们要开始编写Action类了,代码如下:
packagetutorial;
importcom.opensymphony.xwork2.ActionSupport;
publicclassAjaxValidationextendsActionSupport{
privatestaticfinallongserialVersionUID=-7901311649275887920L;
privateStringname;
privateStringpassword;
privateintage;
publicintgetAge(){
returnage;
}
publicvoidsetAge(intage){
this.age=age;
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
publicStringgetPassword(){
returnpassword;
}
publicvoidsetPassword(Stringpassword){
this.password=password;
}
@Override
publicStringexecute(){
returnSUCCESS;
}
}清单3src/tutorial/AjaxValidation.java
上述代码一目了然,相信大家已经很熟悉了。下面,我们再来看看表单校验的配置代码:
<!DOCTYPEvalidatorsPUBLIC"-//OpenSymphonyGroup//XWorkValidator1.0.2//EN""http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<validatortype="regex">
<paramname="fieldName">password</param>
<paramname="expression">
<![CDATA[(?!^[0-9]*$)(?!^[a-zA-Z]*$)^([a-zA-Z0-9]{8,10})$]]>
</param>
<message>Passwordmustbebetween8and10characters,containatleastonedigitandonealphabeticcharacter,andmustnotcontainspecialcharacters</message>
</validator>
<fieldname="name">
<field-validatortype="requiredstring">
<message>Youmustenteraname</message>
</field-validator>
</field>
<fieldname="age">
<field-validatortype="int">
<paramname="min">18</param>
<paramname="max">127</param>
<message>Agemustbebetween18and127</message>
</field-validator>
</field>
</validators>清单4src/tutorial/AjaxValidation-validation.xml
对于AjaxValidation类的name、password和age三个字段,我分别用了非空、正规表达式和范围验证。正规表达式(?!^[0-9]*$)(?!^[a-zA-Z]*$)^([a-zA-Z0-9]{8,10})$的作用是保证密码由至少包括一个数字和一个字母,且不能含有符号的长度为8到10的字符串组成。它也是所谓强密码(StrongPassword)的普通实现。
接下来的是JSP的代码,内容如下:
<%@pagelanguage="java"contentType="text/html;charset=utf-8"
pageEncoding="utf-8"%>
<%@taglibprefix="s"uri="/struts-tags"%>
<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<htmlxmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Struts2AJAX-Validation</title>
<s:headtheme="ajax"/>
</head>
<body>
<h2>
AJAXValidationUsingDWR
</h2>
<s:formmethod="post"validate="true"theme="ajax">
<s:textfieldlabel="Name"name="name"/>
<s:passwordlabel="Password"name="password"/>
<s:textfieldlabel="Age"name="age"/>
<s:submit/>
</s:form>
</body>
</html>清单5WebContent/AjaxValidation.jsp
以上代码也不复杂,不过需要的是注意的是除了要加入<s:headtheme="ajax"/>外,<s:form/>也必须加入validate="true"theme="ajax"的属性。
最后是Struts2的配置文件,内容如下所示:
<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEstrutsPUBLIC
"-//ApacheSoftwareFoundation//DTDStrutsConfiguration2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<packagename="Struts2_AJAX_DEMO"extends="struts-default">
<actionname="AjaxValidation"class="tutorial.AjaxValidation">
<resultname="input">AjaxValidation.jsp</result>
<result>AjaxValidation.jsp</result>
</action>
</package>
</struts>清单6src/struts.xml
最后发布运应用程序,在浏览器地址栏中输入http://localhost:8080/Struts2_Ajax3/AjaxValidation!input.action出现如下图所示页面:
图4AjaxValidation页面输出
在文本框中输入错误的值使页面出现错误提示信息,如下图所示:
图5AjaxValidation页面错误提示
可能有朋友会问怎么知道这是通过AJAX进行校验的呢?在这里我向大家推荐一个AJAX开发必备的工具——Firebug。Firebug是Firefox的一个功能强大的插件,它可以准确地输出和定位Javascript的错误、通过直观的方式查看HTML文档的DOM及其样式、所见即所得的编辑方式,更值得一赞的是它可以方便地对Javascript进行跟踪和调试,如果你希望这进一步了解这个工具,请安装Firefox2.0以上版本,并使用它浏览以下网址http://www.getfirebug.com。
在安装完成Firebug之后,在Firefox中打开http://localhost:8080/Struts2_Ajax3/AjaxValidation!input.action,按“F12”键找开Firebug窗口,如果你第一次使用Firebug,请点击其窗口中的链接“EnableFirebug”激活插件。之后,点击“Net”,并在出现的菜单中点击选中“XHR”。然后将光标移入文本框,再将光标移出使文本框失去焦点,你可以看到Firebug窗口会多出一项记录,如下图所示:
图6Firebug中查看XHR请求
这就证明你在文本框失去焦出时,Struts2会发送XHR请求到服务器以对该文本框值进行校验。有兴趣的朋友可以通过Firebug,研究XHR的请求与响应,这样可以加深对DWR工作原理的理解。
何时使用AJAX表单校验
虽然在Struts2实现AJAX表单校验是一件非常简单的事,但我建议大家不要在所有的场合都使用这个功能,原因可以分为以下几个方面:
AJAX校验在服务器上进行数据校验,可能会比较耗时;
AJAX校验可能会过于频繁,加重服务器的负载;
一些普通的校验,只需要使用纯Javascript便可以实现。
读到这里,有的朋友可能会问:“那么什么时候才应该使用AJAX表单校验呢?”答案其实很简单,当我们的校验在页面加载时还不能够确定的情况下,就应该使用这个功能。例如,注册用户时,校验用户名是否已经存在;或者校验涉及过多的页务逻辑等。
现在让我们来改造一下上述例子,对于name我们可以使用AJAX校验,但对于其它的字段应该使用纯Javascript的校验。
在tutorial.AjaxValidation类加入如下方法:
@Override
publicvoidvalidate(){
Set<String>users=newHashSet<String>();
users.add("max");
users.add("scott");
if(users.contains(name)){
addFieldError("name","Theusernamehasbeenused!");
}
}清单7src/tutorial/AjaxValidation.java代码片段
用于模拟用户注册的场境,当然在真实情况应该在数据库中检查用户是否存在。
接下来再修改JSP文件,将<s:form/>里面的内容改为如下所示代码:
<s:formmethod="post"validate="true"theme="ajax_xhtml">
<s:textfieldlabel="Name"name="name"theme="ajax"/>
<s:passwordlabel="Password"name="password"theme="xhtml"/>
<s:textfieldlabel="Age"name="age"theme="xhtml"/>
<s:submittheme="xhtml"/>
</s:form>清单8WebContent/AjaxValidation.jsp代码片段
对比早前的JSP代码,大家可以看出我将<s:form/>的theme改成了“ajax_xhtml”,这个theme不是Struts2自带,需要自定义。另外,除了Name使用了ajax的theme之外,其它的表单标签的theme都为xhtml,如此一来便可以实现只有当Name文本框失去焦点时才发生AJAX表单校验。
接下来,应该是我们的自定义ajax_xhtml的theme了。在源代码文件夹下新建包“template.ajax_xhtml”,然后在其中加入form.ftl和form-close.ftl文件,内容分别如下:
<#ifparameters.validate?exists>
<scripttype="text/javascript"src="${base}/struts/validationClient.js"></script>
<scripttype="text/javascript"src="${base}/dwr/interface/validator.js"></script>
<scripttype="text/javascript"src="${base}/dwr/engine.js"></script>
<scripttype="text/javascript"src="${base}/struts/ajax/validation.js"></script>
</#if>
<#include"/${parameters.templateDir}/xhtml/form-validate.ftl"/>
<#include"/${parameters.templateDir}/simple/form.ftl"/>
<#include"/${parameters.templateDir}/xhtml/control.ftl"/>清单9src/template/ajax_xhtml/form.ftl
上述的文件与xhtmltheme中的form.ftl文件相似,我只是加入了AJAX表单校验所用的Javascript库,以便theme为ajax的表单标签使用。
<#include"/${parameters.templateDir}/xhtml/control-close.ftl"/>
<#include"/${parameters.templateDir}/simple/form-close.ftl"/>
<#include"/${parameters.templateDir}/xhtml/form-close-validate.ftl"/>清单10src/template/ajax_xhtml/form-close.ftl
这个文件与xhtmltheme中的form-close.ftl文件相同。
最后发布运行应用程序,大家可以发现在Password与Age的校验,只有在表单提交时才发生,而且是纯Javascript的校验。不过,以上代码还不是很完善,在行为上有些BUG。
总结
Struts2相比一些其它的框架,在实现AJAX方面的确简单很多。更激动人心的是Struts2的标签库支持基于模板的输出,使得开发者可以跟据自身的需要方便地改变标签的行为。
在将要发布的Struts2.1版本中,AJAX表单校验将不再使用DWR,统一使用DOJO实现,详情请参考:http://struts.apache.org/2.0.9/docs/ajax-validation.html。
相关推荐
结束数据方法的参数,该如何定义?-- 集合为自定义实体类中的结合属性,有几个实体类,改变下标就行了。<input id="add" type="button" value="新增visitor&quo