1,输入校验介绍
Struts2提供了功能强大的输入校验机制,通过Struts2内建的输入校验器,应用无需书写任何代码,即可完成大部分的校验功能,并可以同时完成客户端和服务器端的校验。如果应用的输入校验规则特别,Struts2也允许通过重写validate方法来完成自定义校验,另外Struts2的开放性还允许开发者提供自定义的校验器。
表现层的数据处理包括两个方面:一是数据类型的转化,因为Web应用接收到的所有数据都是字符串类型的;另一个就是输入校验,因为开发者必须全面考虑用户输入的各种情况,尤其需要注意那些非正常输入。
客户端的校验最基础的方法就是在页面写javascript代码手工校验,服务器端的校验最基础的方法就是在处理请求的Servlet的service()方法中添加校验代码。
Struts2中可以通过重写validate方法来完成输入校验。如果我们重写了validate方法,则该方法会应用于此Action中的所有提供服务的业务方法。Struts2支持校验特定方法的validateXxx()方法。例如某个Action中有一个regist()业务方法,我们可以写一个validateRegist()方法来进行regist()的特殊校验,客户端请求调用的次序如下:validateRegist()--------------->validate()-------------->regist()
Struts2的输入校验流程如下:
1,类型转换器负责对字符串的请求参数执行类型转换,并将这此值设置成Action的属性值。
2,在执行类型转换过程中可能出现异常,如果出现异常,将异常信息保存到ActionContext中,conversionError拦截器负责将其封装到fieldError里,然后执行第3步;如果转换过程没有异常信息,则直接进入第3步。
3,通过反射调用validateXxx()方法,其中Xxx是即将处理用户请求的处理逻辑所对应的方法名。
4,调用Action类里的validate()方法。
5,如果经过上面4步都没有出现fieldError,将调用Action里处理用户请求的处理方法;如果出现了fieldError,系统将转入input逻辑视图所指定的视图资源。
2,java中Struts2的输入校验
1,最基础的Struts2输入校验
Struts2中单独对每一个Action指定一个校验文件,它的命名方式为<Action-name>-validation.xml。它存放在与Action相同的包中,校验只需要配置一个校验文件即可实现。系统的其它地方不需要改动,系统会自动加载该文件。校验文件RegistAction-validation.xml如下例:
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<!-- 校验文件的根元素 -->
<validators>
<!-- 校验Action的name属性 -->
<field name="name">
<!-- 指定name属性必须满足必填规则 -->
<field-validator type="requiredstring">
<!-- 校验前去掉name属性的前后空格 -->
<param name="trim">true</param>
<!-- 提示信息 -->
<message>必须输入名字</message>
</field-validator>
<!-- 指定name属性必须满足匹配指定的正则表达式 -->
<field-validator type="regex">
<param name="expression"><![CDATA[(\w{4,25})]]></param>
<message>您输入的用户名只能是字母和数组,且长度必须在4到25之间</message>
</field-validator>
</field>
<field name="pass">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>必须输入密码</message>
</field-validator>
<field-validator type="regex">
<param name="expression"><![CDATA[(\w{4,25})]]></param>
<message>您输入的密码只能是字母和数组,且长度必须在4到25之间</message>
</field-validator>
</field>
<field name="age">
<!-- 指定age属性必须在指定的范围内 -->
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>年纪必须在1到150之间</message>
</field-validator>
</field>
<field name="birth">
<!-- 指定age属性必须在指定的范围内 -->
<field-validator type="date">
<param name="min">1900-01-01</param>
<param name="max">2050-02-21</param>
<message>年纪必须在${min}到${max}之间</message>
</field-validator>
</field>
</validators>
2,Struts2中输入校验提示信息的国际化
在Struts2的校验中应用国际化也非常简单,请看如下xml配置代码: <field name="name">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message key="name.requried"/>
</field-validator>
<field-validator type="regex">
<param name="expression"><![CDATA[(\w{4,25})]]></param>
<message key="name.regex"/>
</field-validator>
</field>
message元素指定key属性指定的是国际化资源中对应的key。还可以使用以下配置获取国际化资源中的信息:<message>${getText("name.requried")}</message> 这种方式是通过调用ActionSupport类的getText()方法来获取国际化资源的。
3,Struts2中应用客户端输入校验
使用客户端输入校验可以减轻服务器的负担。Struts2对客户端的输入校验进行了封装,使得我们开发时特别容易。
1,JSP页面代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>注册页面</title>
</head>
<body>
<s:form action="regist" validate="true">
<s:textfield label="用户名" name="name"></s:textfield>
<s:password label="密码" name="pass"></s:password>
<s:textfield label="年龄" name="age"></s:textfield>
<s:textfield label="生日" name="birth"></s:textfield>
<s:submit></s:submit>
</s:form>
</body>
</html>
注意这里要用Struts2的标签,form的validate属性要设置为true,并且不要将theme属性指定为simple.(simple表示struts2将把这个解析成普通的HTML标签)
2,校验配置文件:这里的校验配置文件同原先的配置文件并没有不同,但是这里使用<message key="name.requried"/>无法从全局国际化资源中获取信息,只能使用<message>${getText("name.requried")}</message>方式获取国际化资源。
3,部署后的JSP页面代码:
大家可以看到部署后的JSP页面中自动生成了我们在校验配置文件中对应的javascript代码。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>注册页面</title>
</head>
<body>
<script src="/lovely/struts/xhtml/validation.js"></script>
<form namespace="" id="regist" name="regist" οnsubmit="return validateForm_regist();" action="/lovely/regist.action" method="POST">
<table class="wwFormTable">
<tr>
<td class="tdLabel"><label for="regist_name" class="label">用户名:</label></td>
<td
><input type="text" name="name" value="" id="regist_name"/>
</td>
</tr>
<tr>
<td class="tdLabel"><label for="regist_pass" class="label">密码:</label></td>
<td
><input type="password" name="pass" id="regist_pass"/>
</td>
</tr>
<tr>
<td class="tdLabel"><label for="regist_age" class="label">年龄:</label></td>
<td
><input type="text" name="age" value="" id="regist_age"/>
</td>
</tr>
<tr>
<td class="tdLabel"><label for="regist_birth" class="label">生日:</label></td>
<td
><input type="text" name="birth" value="" id="regist_birth"/>
</td>
</tr>
<tr>
<td colspan="2"><div align="right"><input type="submit" id="regist_0" value="Submit"/>
</div></td>
</tr>
</table></form>
<script type="text/javascript">
function validateForm_regist() {
form = document.getElementById("regist");
clearErrorMessages(form);
clearErrorLabels(form);
var errors = false;
// field name: name
// validator name: requiredstring
if (form.elements['name']) {
field = form.elements['name'];
var error = "必须输入用户名!";
if (field.value != null && (field.value == "" || field.value.replace(/^\s+|\s+$/g,"").length == 0)) {
addError(field, error);
errors = true;
}
}
// field name: name
// validator name: regex
if (form.elements['name']) {
field = form.elements['name'];
var error = "您输入的用户名只能是字母和数组,且长度必须在4到25之间";
if (field.value != null && !field.value.match("(\\w{4,25})")) {
addError(field, error);
errors = true;
}
}
// field name: pass
// validator name: requiredstring
if (form.elements['pass']) {
field = form.elements['pass'];
var error = "必须输入密码";
if (field.value != null && (field.value == "" || field.value.replace(/^\s+|\s+$/g,"").length == 0)) {
addError(field, error);
errors = true;
}
}
// field name: pass
// validator name: regex
if (form.elements['pass']) {
field = form.elements['pass'];
var error = "您输入的密码只能是字母和数组,且长度必须在4到25之间";
if (field.value != null && !field.value.match("(\\w{4,25})")) {
addError(field, error);
errors = true;
}
}
// field name: age
// validator name: int
if (form.elements['age']) {
field = form.elements['age'];
var error = "年纪必须在1到150之间";
if (field.value != null) {
if (parseInt(field.value) <
1 ||
parseInt(field.value) >
150) {
addError(field, error);
errors = true;
}
}
}
// field name: birth
// validator name: date
if (form.elements['birth']) {
field = form.elements['birth'];
var error = "年纪必须在00-1-1到50-2-21之间";
}
return !errors;
}
</script>
</body>
</html>
从上面Struts2自动生成的javascript代码可以看到,最后的birth校验并没有按照我们要求的来校验,可见Struts2中并不是所有的服务器端校验都可以转换成客户端校验。客户端校验仅仅支持如下几种校验器:
required validator 必填校验器
requiredstring validator 必填字符串校验器
stringlength validator 字符串长度校验器
regex validator 表达式校验器
email validator 邮件校验器
url validator 网址校验器
int validator 整数校验器
double validator 双精度数校验器
4,为指定的方法配置特殊的校验规则
当一个Action中有多个业务方法时,我们可能需要对其中的某个方法配置单独的校验规则,比如注册时的要求用户两次输入的密码必须相同等,这时我们可以配置一个单独的校验文件,命名规则为:<actionName>-<methodName>-validation.xml,可以看到这里多了一个方法名,这个方法名就是要校验的业务逻辑在struts.xml配置文件中配置的name,这个文件也要同Action放在同一个目录下。
5,多个校验规则文件的搜索次序
例如有一个LoginAction继承BaseAction,这两个Action中都有业务方法login,并且存在4份校验规则文件如下:BaseAction-vadition.xml, BaseAction-login-validation.xml , LoginAction-validation.xml, LoginAction-login-validation.xml那么用户访问LoginAction的login方法里,会按照以上的顺序执行校验规则,实际的校验规则是以上四个校验规则的总和,但是如果存在冲突的情况下,后面的校验规则优先。
6,非字段校验器的配置
Struts2中除了有象我们的配置文件的那种以字段为基础的校验文件的配置外,还有另外一种以校验规则为基础的配置,请看下面的配置文件的代码:
<?xml version="1.0" encoding="GBK"?>
<!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">
<!-- 要校验的属性为name -->
<param name="fieldName">name</param>
<!-- 其它参数 -->
<param name="trim">true</param>
<!-- 返回的提示 -->
<message>${getText("name.requried")}</message>
</validator>
<validator type="regex">
<param name="fieldName">name</param>
<param name="trim">true</param>
<param name="expression"><![CDATA[(\w{4,25})]]></param>
<message>${getText("name.regex")}</message>
</validator>
<validator type="requiredstring">
<param name="fieldName">pass</param>
<param name="trim">true</param>
<message>${getText("pass.requried")}</message>
</validator>
<validator type="regex">
<param name="fieldName">pass</param>
<param name="trim">true</param>
<param name="expression"><![CDATA[(\w{4,25})]]></param>
<message>${getText("pass.regex")}</message>
</validator>
<validator type="int">
<param name="fieldName">age</param>
<param name="min">1</param>
<param name="max">150</param>
<message>${getText("age.range")}</message>
</validator>
<validator type="date">
<param name="fieldName">birth</param>
<param name="min">1900-01-01</param>
<param name="max">2050-02-21</param>
<message>${getText("birth.range")}</message>
</validator>
</validators>
7,短路校验器的配置
Struts2默认的校验配置是非短路的方式,即把一个字段所有的不符合要求的提示都提示用户,我们也可以配置以短路的方式提示用户,即当用户输入的条件不满足第一次校验的时候就返回提示信息,不再往下执行。这种配置方式只能是以字段为基础的校验器。只需要在<field-validator>元素上加上short-circuit="true"即可,例如:
<field name="name">
<field-validator type="requiredstring" short-circuit="true">
<param name="trim">true</param>
<message>${getText("name.requried")}</message>
</field-validator>
<field-validator type="regex">
<param name="expression"><![CDATA[(\w{4,25})]]></param>
<message>${getText("name.regex")}</message>
</field-validator>
</field>
这种方式可能会造成多次提示用户,所以并不推荐使用此方式,并且客户端校验并不支持这种方式。并且采用这种方式之后,校验的顺序将很难控制(主要是按照规则来进行控制,但此时将花费大量时间)。