5.4 使用校验框架完成输入校验
前面介绍了Struts 2中内置的校验器,下面将通过这些校验器来改写注册案例。同时将结合国际化处理来实现国际化信息提示,并通过配置来添加客户端校验。
5.4.1 完成输入校验
前面介绍了Struts 2中内置的校验器。现在开始使用这些校验器来改写以前那个注册案例。不再使用手动添加校验代码的方式,而是使用编写校验规则文件的方式来进行输入校验。
下面来看有哪些输入校验规则。
(1)用户名、密码、确认密码必须输入。
(2)用户名只能是数字或者字母,长度为6~20之间。
(3)密码、确认密码必须是数字或者字母,长度为6~20之间。
(4)密码和确认密码必须相同。
(5)年龄必须为整数而且必须是有效的年龄值。
(6)出生日期必须为正确的日期格式,如1988-01-03,而且只能是1900-1-1~2010-1-1之间。
(7)邮箱地址必须为合法的邮箱地址。
首先新建一个用户注册页。在该注册页中使用Sturts 2的表单标签来设计表单,代码如下所示。
<%@ page language="java" import="java.util.*" pageEncoding="gb2312"%> <%@ taglib prefix="s" uri="/struts-tags" %> <! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>注册页面</title> </head> <body> <! -- 注册表单 --> <center> <h2>注册页面</h2> <s:form action="register"> <s:textfield name="username" label="用户名"></s:textfield> <s:password name="password" label="密码"></s:password> <s:password name="repassword" label="确认密码"></s:password> <s:textfield name="age" label="年龄"></s:textfield> <s:textfield name="birth" label="出生日期"></s:textfield> <s:textfield name="email" label="邮箱地址"></s:textfield> <s:submit value="注册"></s:submit> </s:form> </center> </body> </html>
新建业务控制器Action,类名为RegisterAction。该Action继承ActionSupport类,并添加相应的属性以及属性的setter和getter方法。与前面不同的是,这里不用提供validate()方法,代码如下所示。
package net.hncu.action; import java.util.Calendar; import java.util.Date; import java.util.regex.Pattern; import com.opensymphony.xwork2.ActionSupport; public class RegisterAction extends ActionSupport{ private String username; private String password; private String repassword; private int age; private Date birth; private String email; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getRepassword() { return repassword; } public void setRepassword(String repassword) { this.repassword = repassword; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String execute() throws Exception { return SUCCESS; } }
为RegisterAction添加校验规则文件,文件名为“RegisterAction-validation.xml”。前面介绍了该校验规则文件可以采用两种配置风格。首先采用字段校验器配置风格编写该校验文件,代码如下所示。
<? xml version="1.0" encoding="UTF-8"? > <! DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> <field name="username"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>必须输入用户名</message> </field-validator> <field-validator type="regex"> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message>用户名长度必须为6至20之间</message> </field-validator> </field> <field name="password"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>必须输入密码</message> </field-validator> <field-validator type="regex"> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message>密码长度必须为6至20之间</message> </field-validator> </field> <field name="repassword"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>必须输入确认密码</message> </field-validator> <field-validator type="regex"> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message>确认密码长度必须为6至20之间</message> </field-validator> <field-validator type="fieldexpression"> <param name="expression"><! [CDATA[(repassword==password)]]></param> <message>密码和确认密码必须一致</message> </field-validator> </field> <field name="age"> <field-validator type="int"> <param name="min">1</param> <param name="max">120</param> <message>年龄必须在${min}至${max}之间</message> </field-validator> </field> <field name="birth"> <field-validator type="date"> <param name="min">1900-01-01</param> <param name="max">2010-01-01</param> <message>出生日期必须在1900-01-01至2010-01-01之间</message> </field-validator> </field> <field name="email"> <field-validator type="email"> <message>请输入有效的电子邮箱地址</message> </field-validator> </field> </validators>
同样也可以使用非字段校验器配置风格编写该校验规则文件,代码如下所示。
<? xml version="1.0" encoding="UTF-8"? > <! DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> <validator type="requiredstring"> <param name="fieldName">username</param> <param name="trim">true</param> <message>必须输入用户名</message> </validator> <validator type="regex"> <param name="fieldName">username</param> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message>用户名长度必须为6至20之间</message> </validator> <validator type="requiredstring"> <param name="fieldName">password</param> <param name="trim">true</param> <message>必须输入密码</message> </validator> <validator type="regex"> <param name="fieldName">password</param> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message>密码长度必须为6至20之间</message> </validator> <validator type="requiredstring"> <param name="fieldName">repassword</param> <param name="trim">true</param> <message>必须输入确认密码</message> </validator> <validator type="regex"> <param name="fieldName">repassword</param> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message>确认密码长度必须为6至20之间</message> </validator> <validator type="fieldexpression"> <param name="fieldName">repassword</param> <param name="expression"><! [CDATA[(repassword==password)]]></param> <message>密码和确认密码必须一致</message> </validator> <validator type="int"> <param name="fieldName">age</param> <param name="min">1</param> <param name="max">120</param> <message>年龄必须在${min}至${max}之间</message> </validator> <validator type="date"> <param name="fieldName">birth</param> <param name="min">1900-01-01</param> <param name="max">2010-01-01</param> <message>出生日期必须在1900-01-01至2010-01-01之间</message> </validator> <validator type="email"> <param name="fieldName">email</param> <message>请输入有效的电子邮箱地址</message> </validator> </validators>
不要忘了在“struts.xml”文件中配置RegisterAction,这里不再重复介绍。现在打开用户注册页,不输入任何数据,单击“注册”按钮进行注册。因为输入的用户信息不满足校验规则,从而使得输入校验失败,那么页面将提示要求进行输入的错误信息,如图5.16所示。
同样当输入一些不满足校验规则的用户信息,都会使得输入校验失败,那么页面将跳转回注册页,并提示要求输入合法信息的错误信息,如图5.17所示。
图5.16 注册页面(1)
图5.17 注册页面(2)
5.4.2 增加客户端校验
Struts 2还提供了一个非常好的功能,就是能通过校验规则文件自动添加客户端输入校验。先来看下如何来添加客户端校验。非常简单,只要在<s:form>标签中添加validate=“true”属性即可,代码如下所示。
<s:form action="register" validate="true"> <s:textfield name="username" label="用户名"></s:textfield> <s:password name="password" label="密码"></s:password> <s:password name="repassword" label="确认密码"></s:password> <s:textfield name="age" label="年龄"></s:textfield> <s:textfield name="birth" label="出生日期"></s:textfield> <s:textfield name="email" label="邮箱地址"></s:textfield> <s:submit value="注册"></s:submit> </s:form>
刷新一下用户注册页,并查看该注册页的源代码。从页面源代码中可以看出,Struts 2自动为其注册页面添加了用于客户端校验的JavaScript代码,如下所示。
<script type="text/javascript"> function validateForm_register() { form = document.getElementById("register"); clearErrorMessages(form); clearErrorLabels(form); var errors = false; ... ... // field name: email // validator name: email if (form.elements[‘email' ]) { field = form.elements[‘email' ]; var error = "请输入有效的电子邮箱地址"; if (field.value ! = null && field.value.length > 0 && field.value.match(/\b(^[_A-Za-z0-9-]+(\.[_A-Za-z0-9-]+)*@([A-Za-z0-9-])+(\.[A-Za- z0-9-]+)*((\.[A-Za-z0-9]{2, })—(\.[A-Za-z0-9]{2, }\.[A-Za-z0-9]{2, }))$)\b/gi)==null) { addError(field, error); errors = true; } } return ! errors; } </script>
这时如果输入不符合校验规则的用户信息。页面将提示要求进行输入的错误信息,如图5.18所示。
这里与前面不同的是,这些错误信息是客户端校验产生的。注意观察一下地址栏就会发现,页面仍然是用户注册页,表示并未提交到对应的Action。也就是说校验过程是使用客户端校验来完成的。
那是不是以后都可以不用自己编写客户端校验代码,直接使用Struts 2提供的这个客户端校验功能就行了?其实并不是这样的,起码现在来讲不是这样的。因为Struts 2提供的这个客户端校验功能不是很完美,在很多地方都有欠缺。比如不能检查密码和确认密码是否相同,不能检查日期是否合法等。
图5.18 注册页面(1)
图5.19 注册页面(2)
客户端校验仅仅支持如下校验器。
❑ required(必填校验器)
❑ requiredstring(必填字符串校验器)
❑ int(整数校验器)
❑ double(双精度浮点数校验器)
❑ email(邮箱地址校验器)
❑ url(网址校验器)
❑ regex(正则表达式校验器)
因为客户端校验支持的校验器不多,在开发中无法满足客户端校验规则,所以还是希望能通过自己编写JavaScript代码来实现客户端输入校验。
5.4.3 国际化提示信息
下面来为程序添加国际化功能。首先来编写国际化资源文件,并将错误提示以键值对的形式保存在资源文件中,代码如下所示。
age.rule =年龄必须在${min}至${max}之间 birth.rule = 出生日期必须在1900-01-01至2010-01-01之间 email.rule = 请输入有效的电子邮箱地址 password.required = 必须输入密码 password.rule = 密码长度必须为6至20之间 repassword.equals = 密码和确认密码必须一致 repassword.required = 必须输入确认密码 repassword.rule = 确认密码长度必须为6至20之间 username.required = 必须输入用户名 username.rule = 用户名长度必须为6至20之间
别忘了在“struts.xml”文件中使用全局属性加载该国际化资源文件,代码如下所示。
<constant name="struts.custom.i18n.resources" value="myMessage"></constant>
国际化资源文件创建好了,如何才能让校验规则文件找到对应的错误提示信息呢?只要找到校验规则文件中的message元素,并在该元素中通过key属性指定国际化资源文件中对应的key即可,代码如下所示。
<? xml version="1.0" encoding="UTF-8"? > <! DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> <field name="username"> <field-validator type="requiredstring"> <param name="trim">true</param> <message key="username.required"></message> </field-validator> <field-validator type="regex"> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message key="username.rule"></message> </field-validator> </field> <field name="password"> <field-validator type="requiredstring"> <param name="trim">true</param> <message key="password.required"></message> </field-validator> <field-validator type="regex"> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message key="password.rule"></message> </field-validator> </field> <field name="repassword"> <field-validator type="requiredstring"> <param name="trim">true</param> <message key="repassword.required"></message> </field-validator> <field-validator type="regex"> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message key="repassword.rule"></message> </field-validator> <field-validator type="fieldexpression"> <param name="expression"><! [CDATA[(repassword==password)]]></param> <message key="repassword.equals"></message> </field-validator> </field> <field name="age"> <field-validator type="int"> <param name="min">1</param> <param name="max">120</param> <message key="age.rule"></message> </field-validator> </field> <field name="birth"> <field-validator type="date"> <param name="min">1900-01-01</param> <param name="max">2010-01-01</param> <message key="birth.rule"></message> </field-validator> </field> <field name="email"> <field-validator type="email"> <message key="email.rule"></message> </field-validator> </field> </validators>
这里暂时先去掉客户端校验功能。删除注册页面中fom元素的validate=true属性。现在再打开用户注册页,不输入任何数据,单击“注册”按钮。因为输入的用户信息不满足校验规则,从而使得输入校验失败,那么页面将提示要求进行输入的错误信息,如图5.20所示。同样可以添加英文的资源文件,使得页面能提示英文的错误信息。
5.4.4 客户端校验与国际化问题
之所以开始要去掉客户端校验功能。是因为同时使用客户端校验功能和国际化功能会出现异常。现在为注册页面的form元素添加validate=true属性,增加客户端校验功能。这时再打开用户输入页,发现页面出现了异常,如图5.21所示。
图5.20 注册页面
图5.21 页面异常
这时大家要有耐心,发现问题要想办法解决而不是垂头丧气的。先来看看为什么会出现这样的问题。添加的国际化信息肯定是没有错误的,在使用服务器端校验时已经测试过了。那么是不是使用客户端校验无法找到那些国际化信息呢?好,现在有思路了,通过另一种方式来读取国际化信息。在校验规则文件中使用${getTest(“key”)}来读取国际化文件中的信息,代码如下所示。
<? xml version="1.0" encoding="UTF-8"? > <! DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> <field name="username"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>${getText("username.required")}</message> </field-validator> <field-validator type="regex"> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message>${getText("username.rule")}</message> </field-validator> </field> <field name="password"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>${getText("password.required")}</message> </field-validator> <field-validator type="regex"> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message>${getText("password.rule")}</message> </field-validator> </field> <field name="repassword"> <field-validator type="requiredstring"> <param name="trim">true</param> <message>${getText("repassword.required")}</message> </field-validator> <field-validator type="regex"> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message>${getText("repassword.rule")}</message> </field-validator> <field-validator type="fieldexpression"> <param name="expression"><! [CDATA[(repassword==password)]]></param> <message>${getText("repassword.equals")}</message> </field-validator> </field> <field name="age"> <field-validator type="int"> <param name="min">1</param> <param name="max">120</param> <message>${getText("age.rule")}</message> </field-validator> </field> <field name="birth"> <field-validator type="date"> <param name="min">1900-01-01</param> <param name="max">2010-01-01</param> <message>${getText("birth.rule")}</message> </field-validator> </field> <field name="email"> <field-validator type="email"> <message>${getText("email.rule")}</message> </field-validator> </field> </validators>
上面代码中使用的是在ActionSupport类中的getText方法来确定的国际化文件中的信息。这时再次运行用户注册页,发现不再有异常。同时当输入不符合校验规则的用户信息时,页面将提示相应的错误信息。同样,通过观察浏览器地址栏可以发现这些错误信息是客户端校验产生的,如图5.22所示。
图5.22 注册页面
5.4.5 校验短路
有一点不能忽略掉,就是当不输入任何信息,直接单击“注册”按钮时进行注册时,发现“用户名”文本框上显示了两条校验错误提示信息。这种提示信息是非常不友好的,用户还没有输入用户名,就提示用户名长度必须为6~20之间。
显然这样的错误信息太不友好了,那么有什么方式来解决呢?首先来想想看,为什么会出现两条错误信息。因为是针对一个字段两个校验器都校验失败了,这样才会有两条错误信息。所以就可以采用一种短路的方式来解决这个问题。短路,也就是说一个校验器校验失败了另一个校验器不会再进行校验。
要想实现短路这种效果,只需在field-validtor元素(字段校验器配置风格)或者是valitor元素(非字段校验器风格)中增加一个short-circuit属性,并设置其属性值为true就行了,代码如下所示。
<? xml version="1.0" encoding="UTF-8"? > <! DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> <field name="username"> <field-validator type="requiredstring" short-circuit="true"> <param name="trim">true</param> <message>${getText("username.required")}</message> </field-validator> <field-validator type="regex"> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message>${getText("username.rule")}</message> </field-validator> </field> <field name="password"> <field-validator type="requiredstring" short-circuit="true"> <param name="trim">true</param> <message>${getText("password.required")}</message> </field-validator> <field-validator type="regex"> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message>${getText("password.rule")}</message> </field-validator> </field> <field name="repassword"> <field-validator type="requiredstring" short-circuit="true"> <param name="trim">true</param> <message>${getText("repassword.required")}</message> </field-validator> <field-validator type="regex" short-circuit="true"> <param name="expression"><! [CDATA[(\w{6,20})]]></param> <message>${getText("repassword.rule")}</message> </field-validator <field-validator type="fieldexpression"> <param name="expression"><! [CDATA[(repassword==password)]]></param> <message>${getText("repassword.equals")}</message> </field-validator> </field> <field name="age"> <field-validator type="int"> <param name="min">1</param> <param name="max">120</param> <message>${getText("age.rule")}</message> </field-validator> </field> <field name="birth"> <field-validator type="date"> <param name="min">1900-01-01</param> <param name="max">2010-01-01</param> <message>${getText("birth.rule")}</message> </field-validator> </field> <field name="email"> <field-validator type="email"> <message>${getText("email.rule")}</message> </field-validator> </field> </validators>
这时再打开用户注册页,不输入任何数据,单击“注册”按钮进行注册。现在错误提示不再有两条,而只有一条错误信息,如图5.23所示。
图5.23 注册页面
注意
客户端校验不支持校验器短路,校验器短路暂时只能在服务器端校验中使用。
5.4.6 校验规则文件搜索规则
在前面曾提到过在Action中可以包含多个处理逻辑,不同的处理逻辑可能需要不同的校验规则,假设现在有如下“struts.xml”文件。
<struts> <package name="struts2" extends="struts-default"> <! -- 定义register的Action,其实现类为net.hncu.action.RegisterAction--> <action name="register" class="net.hncu.action.RegisterAction"> <! -- 定义处理结果与视图资源之间的关系--> <result name="success">/result.jsp</result> <result name="input">/register.jsp</result> </action> <! -- 定义deleteUser的Action,其实现类为net.hncu.action.RegisterAction, 采用deleteUser方法来处理--> <action name="deleteUser" class="net.hncu.action.RegisterAction" method="deleteUser"> <result name="success">/result.jsp</result> <result name="input">/register.jsp</result> </action> </package> </struts>
这时如果要为deleteUser处理逻辑添加校验规则文件,只需要创建如下文件名格式的文件。
<ActionName>-<ActionAliasName>-validation.xml
其中ActionName对应处理Action的类名。ActionAliasName是该处理逻辑在“struts.xml”中对应的name属性。如果需要为deleteUser处理逻辑添加校验规则,校验规则文件名为“RegisterAction-deleteUser-validation.xml”,同样将该文件存放到对应Action编译后的classe文件目录中。
这里同样要注意的是,对deleteUser处理逻辑进行校验时,会先后调用“RegisterAction-validation.xml”和“RegisterAction-deleteUser-validation.xml”这两个校验规则。