(根据 Struts Validator Guide )
作者:
David Winterfeldt 大卫
James Turner 詹姆斯
Rob Leland 罗伯特
翻译:
侯思超
从 0.5 版,验证器在一些 form 中就已经实现了,他最初包含在开发人员包中,后来核心代码挪到 Jakarta Commons 包中和 Struts 特别扩展中作为 Struts 1.1 的一部分。许多开发者为方便一直使用 struts 验证器,这篇文档首先概述验证器的核心功能性,然后大概介绍在 struts1.1 中的变化和新增功能。
如果你配置好验证器插件,你应该扩展 ValidatorForm 而不是 ActionForm ,以便它能加载你的 Validator 资源。他根据 struts-config.xml 文件中的 action 的 name 属性为当前 form 的调用相应的验证器,因此在 validator-rules.xml 中的 form 元素的名称属性应该与 action 的 name 属性值相匹配。
另外一种选择是扩展 ValidatorActionForm 而不是 ValidatorForm , ValidatorActionForm 使用 struts-config.xml 中 action 的 path 属性,所以 path 属性的值相应的应该与 validator-rules.xml 中的 Form 的 name 属性匹配。
一个分离的 action 可以定义给多页 form 的每个页面,而且验证规则可以与 action 关联而不是与页码,就像验证范例中的多页 form 范例那样。
在 validator-rules.xml 文件中 form 的验证规则可以组织为 FormSet 。 FormSet 有与 java.util.Locale 类相应的属性:如语言 , 国家以及变量型属性,如果他们未定义, FormSet 将把它设置为默认值。一个 FormSet 也可以有关联的常量。另外还可以定义与 FormSet 同一级别的全局 global 元素,他与 FormSet 同样也有常量。
注意 :你必须在国际化的 FormSet 前声明一个没有国际化的默认 FormSet 。这样如果 Validator 没有找到 locale 时可以有一个默认版本。
可插入验证器的默认错误信息值可以被 msg 元素覆盖。所以为 mask 验证器生成错误信息的替代方法就是使用 msg 属性,如果字段的 name 属性与验证器的 name 属性匹配,那末将使用字段的 msg 属性。
error messages 的可以设置 arg0-arg3 等参数元素。如果没有设置 arg0-arg3 的 name 属性, error messages 将使用他们作为默认的构建参数值。如果设置了 name 属性,你就可以把参数指定给一特定的可插入验证器,然后这些参数将在构造错误信息时被使用。
<field
property="lastName"
depends="required,mask">
<msg
name="mask"
key="registrationForm.lastname.maskmsg"/>
<arg0 key="registrationForm.lastname.displayname"/>
<var>
<var-name>mask</var-name>
<var-value>^[a-zA-Z]*$</var-value>
</var>
</field>
默认的 arg0-arg3 元素将在消息资源中查找相应的 key ,如果资源属性设为 false ,她将把值直接传进去,而不从消息资源中查找。注意 1.1 版本中,你必须为每个模块中明确地定义在验证中用到的消息资源,否则将使用 top-level 资源。
<field
property="integer"
depends="required,integer,intRange">
<arg0 key="typeForm.integer.displayname"/>
<arg1
name="range"
key="${var:min}"
resource="false"/>
<arg2
name="range"
key="${var:max}"
resource="false"/>
<var>
<var-name>min</var-name>
<var-value>10</var-value>
</var>
<var>
<var-name>max</var-name>
<var-value>20</var-value>
</var>
</field>
全局的常量可以在全局标签中定义, FormSet/ 本地常量能在 formset 标签中创建。常量当前仅仅是代替字段的 property 属性,字段的 var 元素的 value 属性,字段的 msg 元素的 key 属性,字段的 arg0-arg3 元素的 key 属性。字段的变量也可以在 arg0-arg3 元素中被代替(例如: ${var:min} ))。替换的顺序是 FormSet/Locale 常量第一,全局的常量第二,
arg elements 变量最后。
<global>
<constant>
<constant-name>zip</constant-name>
<constant-value>^\d{5}(-\d{4})?$</constant-value>
</constant>
</global>
<field
property="zip"
depends="required,mask">
<arg0 key="registrationForm.zippostal.displayname"/>
<var>
<var-name>mask</var-name>
<var-value>${zip}</var-value>
</var>
</field>
验证器可以使用字段下面的变量部分来存储变量,这些变量通过字段的 getVar ( (String key) 方法取得。
<field
property="integer"
depends="required,integer,intRange">
<arg0 key="typeForm.integer.displayname"/>
<arg1
name="range"
key="${var:min}" resource="false"/>
<arg2
name="range"
key="${var:max}" resource="false"/>
<var>
<var-name>min</var-name>
<var-value>10</var-value>
</var>
<var>
<var-name>max</var-name>
<var-value>20</var-value>
</var>
</field>
使用 validwhen 来设计复杂验证的一个经常的要求就是根据一个字段验证另外一个字段(比如 , 如果你要用户两次输入口令来确认值口令一致),另外一个就是表单中的一个字段只有另外一个字段有确定值的时候才是必须输入的。新的 validwhen 验证规则将很快被包含在 1.1 后的 STRUTS 版本中,她就是用来处理这种情况的。
validwhen 规则处理单个的变量字段,叫测试。这变量的值是一个布尔的表达式,如果验证有效则它必须为真。可以包含这种变量的表达式有:
u 单引号或双引号字符串 literals ,
u 十进制、十六进制、八进制的 Integer literals ,
u null 与 null 和空字符串匹配,
u 其它可以用属性名引用的 form 字段 , 例如 customerAge ,
u 可以在外部因用得索引字段 , 例如 childLastName[2] ,
u 可以默认 implicit 因用得索引字段 , 例如 childLastName[], 她将作为被索引的字段使用同样的索引到数组中,
The literal * 这里指它包含当前测试字段的值,
作为例子,考虑一个包含通讯地址和邮箱字段的 form 。如果通讯地址不为空则邮箱字段是必须的 required 。你能这样定义 validwhen 规则:
<field property="emailAddress" depends="validwhen">
<arg0 key="userinfo.emailAddress.label"/>
<var>
<var-name>test</var-name>
<var-value>((sendNewsletter == null) or (*this* != null))</var-value>
</var>
</field>
上面定义的意思是:如果通讯地址是空或不空时这个字段时有效的。
这里有个稍微复杂的例子,它使用了索引字段。假定有一个表单,允许用户输入他们希望定购的部件号和数量。类 orderLine 的 bean 的一数组被用来在称为 orderLines 的一属性保持输入项。
If you wished to verify that every line with part number also had a quantity entered, you could do it with:
如果你希望校验订单中有数量输入得每一行,你可以这样:
<field
property="quantity"
indexedListProperty="orderLines"
depends="validwhen">
<arg0 key="orderform.quantity.label"/>
<var>
<var-name>test</var-name>
<var-value>((orderLines[].partNumber == null) or (*this* != null))</var-value>
</var>
</field>
这里的意思是:如果相应的 partNumber 字段是空 , 或这字段是不空的,则这字段是有效的。
最后一个例子,想象一表单,用户必须输入他们的以英寸为单位的高度,如果他们在高度在 60 英寸以下,则出一错误。( it is an error to have checked off nbaPointGuard as a career. )
<field property="nbaPointGuard" depends="validwhen">
<arg0 key="careers.nbaPointGuard.label"/>
<var>
<var-name>test</var-name>
<var-value>((heightInInches >= 60) or (*this* == null))</var-value>
</var>
</field>
给程序员的简单说明:
所有的比较关系必须在 parens 封装。 All comparisons must be enclosed in parens.
只有两个 itme 时可以 and 或 or 链接。
如果比较的两 item 都可以转为整数,则使用 numeric 比较,否则使用字符串比较。
验证是从 validation.xml 文件中加载的,默认的验证规则定义在 validation.xml 文件中,默认定义了 required, mask ,byte, short, int, long, float, double, date ( 没有本地支持 ), and a numeric range 。
" mask " 方式依赖于默认值安装要求,那意味着 "required " 可以完成,在 "'mask " 将运行以前 "required " 和 " mask " 方式被默认包含进框架中了。任何字段如果不是 "required " 而且是空或有零长度将跳过其他验证。
如果使用了 Javascript 标签,客户端 javascript 在 validator's javascript 属性中查找值而且产生一个有验证 form 方法的对象,要得到更多的关于 Javascript Validator 标签工作细节的详细的解释 , 参阅 html 标签 API 参考。
"'mask' " 方式让你用一正则表达式掩码验证字段,它使用 jakarta 的正规表达式包,所有的有效性规则存储在 validator-rules.xml 文件,使用的主类是 org.apache.regexp.RE 。
validation.xml 文件中的验证器配置范例:
<validator name="required"
classname="org.apache.struts.validator.FieldChecks"
method="validateRequired"
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.apache.struts.action.ActionErrors,
javax.servlet.http.HttpServletRequest"
msg="errors.required">
<validator name="mask"
classname="org.apache.struts.validator.FieldChecks"
method="validateMask"
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.apache.struts.action.ActionErrors,
javax.servlet.http.HttpServletRequest"
msg="errors.invalid">
方法的参数是用逗号分隔的一些类名称列表,方法属性需要有一个符合上面的列表的签名。列表由以下组合而成:
java.lang.Object – 要验证的 Bean 。
org.apache.commons.validator.ValidatorAction – 当前 ValidatorAction 。
org.apache.commons.validator.Field – 要验证的字段
org.apache.struts.action.ActionErrors – 如果验证错误将加入 ActionError 的错误对象 javax.servlet.http.HttpServletRequest – 当前 request 对象。
javax.servlet.ServletContext – 应用的 ServletContext 。
org.apache.commons.validator.Validator– 当前的 org.apache.commons.validator.Validator 实例。
java.util.Locale – 当前用户的 Locale 。
字段部分有一可选的页面属性,它可以被设为整数,页上字段的所有验证小于或等于服务器端验证的当前页,页上字段的所有验证小于或等于客户端页上所有字段的验证小于或等于服务器端验证的当前页验证的当前页。一个 mutli-part 表单需要定义页面属性:
<html:hidden property="page" value="1"/> 。
这是一个展示你怎样才能比较两个字段是否有一样的值的例子。比如“用户改变他们的口令“一般会有口令字段和一确认字段。
<validator name="twofields"
classname="com.mysite.StrutsValidator"
method="validateTwoFields"
msg="errors.twofields"/>
<field property="password" depends="required,twofields">
<arg0 key="typeForm.password.displayname"/>
<var>
<var-name>secondProperty</var-name>
<var-value>password2</var-value>
</var>
</field>
public static boolean validateTwoFields(
Object bean, ValidatorAction va,
Field field, ActionErrors errors, HttpServletRequest request,
ServletContext application) {
String value = ValidatorUtils.getValueAsString( bean, field.getProperty());
String sProperty2 = field.getVarValue("secondProperty");
String value2 = ValidatorUtils.getValueAsString( bean, sProperty2);
if (!GenericValidator.isBlankOrNull(value)) {
try {
if (!value.equals(value2)) {
errors.add(field.getKey(),
Resources.getActionError( application, request, va, field));
return false;
}
} catch (Exception e) {
errors.add(field.getKey(), Resources.getActionError( application, request, va, field));
return false;
}
}
}
Struts Validator 依赖于 Commons Validator 包,所以问题报告和增强需求可能在两个产品中列出。
· Struts Validator Bugzilla Reports
· Commons Validator Bugzilla Reports
新建的标记属性。
<html:javascript> 标记有新的属性定义 .
使用 commons-validator.jar 中的 DTD 验证。
当前使用的验证 XML 文件是根据 commons-validator.jar 中的 DTD 。 Struts 不在为 validator-rules.xml and validator.xml. 单独维护一个分离的 DTD ,另外 ,commons-validator 现在维护一个统一的 validator.dtd 。修改所有 validator.xml 文件的 DTD 引用为
<!DOCTYPE form-validation PUBLIC
"-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.0//EN"
"http://jakarta.apache.org/commons/dtds/validator_1_0.dtd">
空字段。
当前默认在所有得基础验证类型中忽略空白的字段,如果你要求一个字段必须输入,那末在你的应用的 validator.xml 文件相应的字段定义的 depends 属性中添加 " required " 。
新建的范围 RANGE 方法 .
JavaScript 和 JAVA 中都添加了 intRange & floatRange 方法。
有条件地 REQUIRED 字段 .
最大的修改是添加了基于其她字段的值的有条件地 require 验证的能力。它允许你定义逻辑如:“只有 X 字段非空的时候 Y 字段为 ’male’ 才有效”,这是实现上述逻辑的推荐方法,这种方法在 1.1 版后的第一版将实现。在 1.1 版中添加的 Requiredif 验证规则,将在新版中去掉。不过,如果你正准备使用 requiredif ,这里有一个简短的教程。
让我们假定你有一个有 3 个字段的医药的信息表单,性别 sex ,怀孕测试 pregnancyTest ,测试结果 testResult ,如果性别为 'f' or 'F' ,则怀孕测试 pregnancyTest 是 required ,如果 pregnancyTest 不是空,测试结果 testResult 是 required 。
你的 validation.xml 文件的输入项应该是这样的:
<form name="medicalStatusForm">
<field property="pregnancyTest" depends="requiredif">
<arg0 key="medicalStatusForm.pregnancyTest.label"/>
<var>
<var-name>field[0]</var-name>
<var-value>sex</var-value>
</var>
<var>
<var-name>fieldTest[0]</var-name>
<var-value>EQUAL</var-value>
</var>
<var>
<var-name>fieldValue[0]</var-name>
<var-value>F</var-value>
</var>
<var>
<var-name>field[1]</var-name>
<var-value>sex</var-value>
</var>
<var>
<var-name>fieldTest[1]</var-name>
<var-value>EQUAL</var-value>
</var>
<var>
<var-name>fieldValue[1]</var-name>
<var-value>f</var-value>
</var>
<var>
<var-name>fieldJoin</var-name>
<var-value>OR</var-value>
</var>
</field>
<field property="testResult" depends="requiredif">
<arg0 key="medicalStatusForm.testResult.label"/>
<var>
<var-name>field[0]</var-name>
<var-value>pregnancyTest</var-value>
</var>
<var>
<var-name>fieldTest[0]</var-name>
<var-value>NOTNULL</var-value>
</var>
</field>
</form>
这里有一个使用索引的属性更复杂的例子,如果你的 struts-config.xml 有这下面:
<form-bean name="dependentlistForm"
type="org.apache.struts.webapp.validator.forms.ValidatorForm">
<form-property
name="dependents"
type="org.apache.struts.webapp.validator.Dependent[]" size="10"/>
<form-property name="insureDependents" type="java.lang.Boolean" initial="false"/>
</form-bean>
这里 dependentlistForm bean 有 lastName , firstName , dob , coverageType 四个属性,你可以这样定义一验证规则:
<form name="dependentlistForm">
<field
property="firstName" indexedListProperty="dependents" depends="requiredif">
<arg0 key="dependentlistForm.firstName.label"/>
<var>
<var-name>field[0]</var-name>
<var-value>lastName</var-value>
</var>
<var>
<var-name>fieldIndexed[0]</var-name>
<var-value>true</var-value>
</var>
<var>
<var-name>fieldTest[0]</var-name>
<var-value>NOTNULL</var-value>
</var>
</field>
<field
property="dob" indexedListProperty="dependents" depends="requiredif,date">
<arg0 key="dependentlistForm.dob.label"/>
<var>
<var-name>field[0]</var-name>
<var-value>lastName</var-value>
</var>
<var>
<var-name>fieldIndexed[0]</var-name>
<var-value>true</var-value>
</var>
<var>
<var-name>fieldTest[0]</var-name>
<var-value>NOTNULL</var-value>
</var>
</field>
<field
property="coverageType" indexedListProperty="dependents" depends="requiredif">
<arg0 key="dependentlistForm.coverageType.label"/>
<var>
<var-name>field[0]</var-name>
<var-value>lastName</var-value>
</var>
<var>
<var-name>fieldIndexed[0]</var-name>
<var-value>true</var-value>
</var>
<var>
<var-name>fieldTest[0]</var-name>
<var-value>NOTNULL</var-value>
</var>
<var>
<var-name>field[1]</var-name>
<var-value>insureDependents</var-value>
</var>
<var>
<var-name>fieldTest[1]</var-name>
<var-value>EQUAL</var-value>
</var>
<var>
<var-name>fieldValue[1]</var-name>
<var-value>true</var-value>
</var>
<var>
<var-name>fieldJoin</var-name>
<var-value>AND</var-value>
</var>
</field>
</form>
这里的意思是:
如果 lastName 字段是