Struts Validator验证器使用指南
(根据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属性,你就可以把参数指定给一特定的可插入验证器,然后这些参数将在构造错误信息时被使用。
property="lastName"
depends="required,mask">
name="mask"
key="registrationForm.lastname.maskmsg"/>
mask
^[a-zA-Z]*$
默认的arg0-arg3元素将在消息资源中查找相应的key,如果资源属性设为false,她将把值直接传进去,而不从消息资源中查找。注意1.1版本中,你必须为每个模块中明确地定义在验证中用到的消息资源,否则将使用top-level资源。
property="integer"
depends="required,integer,intRange">
name="range"
key="${var:min}"
resource="false"/>
name="range"
key="${var:max}"
resource="false"/>
min
10
max
20
常量/变量
全局的常量可以在全局标签中定义,FormSet/本地常量能在formset 标签中创建。常量当前仅仅是代替字段的property属性,字段的var 元素的 value属性,字段的msg 元素的 key属性,字段的arg0-arg3 元素的 key属性。字段的变量也可以在arg0-arg3 元素中被代替(例如:${var:min}))。替换的顺序是FormSet/Locale常量第一,全局的常量第二,
arg elements 变量最后。
zip
^\d{5}(-\d{4})?$
property="zip"
depends="required,mask">
mask
${zip}
验证器可以使用字段下面的变量部分来存储变量,这些变量通过字段的getVar((String key)方法取得。
property="integer"
depends="required,integer,intRange">
name="range"
key="${var:min}" resource="false"/>
name="range"
key="${var:max}" resource="false"/>
min
10
max
20
使用validwhen设计复杂的验证
使用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 规则:
test
((sendNewsletter == null) or (*this* != null))
上面定义的意思是:如果通讯地址是空或不空时这个字段时有效的。
这里有个稍微复杂的例子,它使用了索引字段。假定有一个表单,允许用户输入他们希望定购的部件号和数量。类orderLine 的bean的一数组被用来在称为orderLines 的一属性保持输入项。
If you wished to verify that every line with part number also had a quantity entered, you could do it with:
如果你希望校验订单中有数量输入得每一行,你可以这样:
property="quantity"
indexedListProperty="orderLines"
depends="validwhen">
test
((orderLines[].partNumber == null) or (*this* != null))
这里的意思是:如果相应的partNumber 字段是空, 或这字段是不空的,则这字段是有效的。
最后一个例子,想象一表单,用户必须输入他们的以英寸为单位的高度,如果他们在高度在60英寸以下,则出一错误。(it is an error to have checked off nbaPointGuard as a career.)
test
((heightInInches >= 60) or (*this* == null))
给程序员的简单说明:
所有的比较关系必须在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文件中的验证器配置范例:
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">
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。
多页面form
字段部分有一可选的页面属性,它可以被设为整数,页上字段的所有验证小于或等于服务器端验证的当前页,页上字段的所有验证小于或等于客户端页上所有字段的验证小于或等于服务器端验证的当前页验证的当前页。一个mutli-part表单需要定义页面属性:
。
比较两个字段
这是一个展示你怎样才能比较两个字段是否有一样的值的例子。比如“用户改变他们的口令“一般会有口令字段和一确认字段。
classname="com.mysite.StrutsValidator"
method="validateTwoFields"
msg="errors.twofields"/>
secondProperty
password2
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;
}
}
}
已知的bug
Struts Validator依赖于Commons Validator包,所以问题报告和增强需求可能在两个产品中列出。
· Struts Validator Bugzilla Reports
· Commons Validator Bugzilla Reports
变更和deprecations
新建的标记属性。
标记有新的属性定义.
使用commons-validator.jar中的DTD验证。
当前使用的验证XML文件是根据commons-validator.jar中的DTD。Struts不在为validator-rules.xml and validator.xml.单独维护一个分离的DTD,另外,commons-validator 现在维护一个统一的validator.dtd。修改所有validator.xml文件的DTD引用为
"-//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 文件的输入项应该是这样的:
field[0]
sex
fieldTest[0]
EQUAL
fieldValue[0]
F
field[1]
sex
fieldTest[1]
EQUAL
fieldValue[1]
f
fieldJoin
OR
field[0]
pregnancyTest
fieldTest[0]
NOTNULL
这里有一个使用索引的属性更复杂的例子,如果你的struts-config.xml 有这下面:
type="org.apache.struts.webapp.validator.forms.ValidatorForm">
name="dependents"
type="org.apache.struts.webapp.validator.Dependent[]" size="10"/>
这里dependentlistForm bean有lastName,firstName,dob,coverageType四个属性,你可以这样定义一验证规则:
property="firstName" indexedListProperty="dependents" depends="requiredif">
field[0]
lastName
fieldIndexed[0]
true
fieldTest[0]
NOTNULL
property="dob" indexedListProperty="dependents" depends="requiredif,date">
field[0]
lastName
fieldIndexed[0]
true
fieldTest[0]
NOTNULL
property="coverageType" indexedListProperty="dependents" depends="requiredif">
field[0]
lastName
fieldIndexed[0]
true
fieldTest[0]
NOTNULL
field[1]
insureDependents
fieldTest[1]
EQUAL
fieldValue[1]
true
fieldJoin
AND
这里的意思是:
如果lastName 字段是非空的,firstName 字段required。因为字段Indexed 为真,这它意味着lastName的indexed 必须与firstName 的索引的一样,dob同理,除非date不为空。
如果lastName 用样索引时的值不空, 而且非索引字段insureDependents为真,则coverageType 是only require。
你可以对字段在[n]中使用任意数字,唯一的限制是他们必须都是AND或OR,你无法混合使用。
Deprecation:
u JavaScript 和Java的range方法.
u StrutsValidator &StrutsValidatorUtil 类中的Deprecation方法
验证器api指南
一个简明的Struts验证器API指南 可以帮助你开始。
验证器资源
Struts Validator: Validating Two Fields Match 作者Matt Raible。(两个字段匹配验证)关于使用方法的文章。(范例部分为翻译此文内容)
DynaForms and the Validator 作者James Turner and Kevin Bedell。Struts Kickstart的其中一章(动态form和验证器),可以自由下载PDF).
Validating user input 作者 David Winterfeldt and Ted Husted。Struts in Action的其中一章,可以自由下载(PDF)。
使用方法
作者:
丑陋 && Snowtears:经过2周的不懈努力,阅读了大量的资料,终于对Validator有了个初步的认识,整理了一下,浅浅的谈了谈写法,希望能有一定的帮助,其中肯定有许多说的不对不准确的地方,还请多指教 real_herozx@163.net
王艺:
根据以上两位的文章正理而成
配置ruts-config.xml:
1、 添加ApplicationResources配置文件。
如:
其中com.sibss.om.struts.ApplicationResources"的部分是资源文件的路径,此文件的作用是提供错误信息的非编程定制化和多语言支持。如果我们使用中文平台操作系统,则默认情况下将首先查找ApplicationResource_zh_CN.properties文件,然后是ApplicationResources_zh.properties,如果前两个文件没有被找到则将查找ApplicationResources.properties文件。
为了能够在页面上显示错误提示信息,我们还需要将以下内容添加到ApplicationResources.properties文件的末尾:
errors.required={0} is required.
errors.minlength={0} cannot be less than {1} characters.
errors.maxlength={0} cannot be greater than {2} characters.
errors.invalid={0} is invalid.
errors.byte={0} must be an byte.
errors.short={0} must be an short.
errors.integer={0} must be an integer.
errors.long={0} must be an long.
errors.float={0} must be an float.
errors.double={0} must be an double.
errors.date={0} is not a date.
errors.range={0} is not in the range {1} through {2}.
errors.creditcard={0} is not a valid credit card number.
errors.email={0} is an invalid e-mail address.
以上仅是struts现在支持的错误类型的错误提示信息,如果你自定义了新类型的错误验证,则还需要在此加上你自己的内容。
以上内容中的{0}指的是错误提交的参数。比如:当你需要页面上的“用户名”不能为空时(也就是上面的errors.required),这个{0}就代表“用户名”,所以如果你没有填写用户名将抛出如下错误:
用户名 is required.(你可以根据需要修改称中文)
我们可能已经注意到了,既然错误提示信息需要配置,那么上例中“用户名”系统是如何得到的呢?没错!也是通过修改此配置文件,内容如下:
visitCust.error.name.required=
用户名
这样当“用户名”为空时,struts后台程序将联合以上两处定义显示错误信息。
另外,上面的“visitCust.error.name.required”是在Validation.xml配置验证内容时指定的。具体见以下介绍。
注意:一般情况下,你的系统只需要一个ApplicationResources文件,所以开发组的成员不要添加自己的resource文件。只有在你的项目分组开发时才需要使用多个ApplicationResources文件,但是,同时你的struts-config.xml文件也会有相同的数量对应。
2、 在struts-config.xml文件中加入validator插件:
加入这个插件后你的应用就具备使用Validator的环境,如:
这里如果是想使用多个***.xml文件的话,value部分写法如下value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml, /WEB-INF/validation1.xml , /WEB-INF/validation2.xml "
在里,定义需要验证的画面对应的Action的时候要加上validate="true"
四种使用方法
1、 用Javascript在客户端进行验证
配置:在需要验证的JSP文件中写入
这里的XXX 是与要进行验证的 forward name,validateXXXX (this);里面的XXXX是需要进行验证的ActionForm名。
在validation.xml文件中写入验证代码就可以进行基本的验证了。这种方法是在客户端进行验证,客户端可以看到JAVASCRIPT部分的全代码。安全性不高
2、 ValidatorForm的validate方法
1、validate()方法:使自己的ActionForm继承ValidatorForm类,在里面编写自己的方法:
public ActionErrors validate (ActionMapping mapping,HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
。。。。。。
if ( mytext.equals("aaa") ) {
//my exampleerrors.add("mytext",new ActionError("mytext.error"));
}
。。。。。。
return errors;
}
此时,如果写了这个方法,就会屏蔽掉在Validation.xml中定义的验证部分,换句话说就是系统运行时,Validation.xml里对应此ActionForm的定义的错误验证部分不实行,如果不写这个方法的话,系统运行时会进行Validation.xml里对应此ActionForm的定义的错误验证部分的操作。此类方法是在服务器端进行验证,验证部分代码客户端不可见。
2、创建你的ActionForm并让它继承org.apache.struts.validator.ValidatorForm类。创建你的Action实现,并和上面定义的ActionForm关联。这里需要注意的是,在定义此Action时一定将validate属性设置为true,并且在你定义的ActionForm中不要实现它的validate方法――这就意味着你将使用ValidatorForm的validate方法,这样才能保证你的错误验证正常进行。配置validation.xml文件。基本内容如下:
depends="required,maxlength">校验内容
ApplicationResource文件中对应
确定最长限制的长度
maxlength
20
注意:此处的arg0和arg1就代表了ApplicationResources文件中使用“{}”括起来的参数。比如:
errors.range={0} is not in the range {1} through {2}.
定义了三个参数,所以你这里也要定义、、三个参数才能完整的显示错误信息。
errors.maxlength={0} cannot be greater than {2} characters.
定义了0、2两个参数,所以你就需要定义和两个参数。
depends="required,maxlength">
maxlength
80
depends="email">
depends="required">
在校验页面的前添加如下内容:
3、 DynaValidatorForm
不需要再写对应的ActionForm,只需在struts-config.xml里把自己的ActionForm进行配置:
在form-property里设置相应的项目,比如说mytext,mytextarea什么的,执行的时候会动态生成ActionForm,再在validation.xml里写入所希望的验证代码,就可以了。JSP文件里不需要写入任何东西,验证也是在服务器端进行,验证部分代码在JSP中不可见。
4、 组合验证
如果使用动态验证DynaValidatorForm的话,不许编写自己的对应的ActionForm,相应的特殊验证会受到相当程度的限制。这个时候,需要将特殊验证部分写入对应的Action,
if(mytext.equals("aaa")){//My Example
ActionErrors errors = new ActionErrors();
errors.add("***",new ActionError("***.error"));
saveErrors(request,errors);
return (mapping.findForward("false"));
}
就可以实现特殊验证了。
实际上你的FORM还可以继承ValidatorActionForm和DynaValidatorActionForm,这两种与他们对应的ValidatorForm和DynaValidatorForm的唯一区别正如开篇就讲到的:在struts-config.xml中查找对应的FORM类时,前者根据ACTION的PATH值,而后者使用NAME值。
范例:
Struts 验证器:验证两个字段匹配
在使用指南中,有一节讲述怎样创建验证器来验证两个字段匹配,我用这个服务器端验证器(象例子中显示的那样)做口令,确定口令验证。这个已经可以正常工作了;但我还想用客户端的javascript验证器来试一试。我写了自己的程序来比较两个字段,但他们和推荐给你的那个不同(from validator-rules.xml)。所以昨天,我补充了怎样添加JavaScript方法到validator-rules.xml。 这里就是怎样配置的的整个过程(大部分在使用指南中已经包含了,保存JavaScript)。
怎样添加两个字段的验证器
Step 1: 生成一个包含validateTwoFields方法的类。在我的代码重,我的类定义为ValidationUtil,他有下列方法:
public static boolean validateTwoFields(
Object bean,
ValidatorAction va,
Field field,
ActionErrors errors,
HttpServletRequest request) {
String value = ValidatorUtil.getValueAsString(bean, field.getProperty());
String sProperty2 = field.getVarValue("secondProperty");
String value2 = ValidatorUtil.getValueAsString(bean, sProperty2);
if (!GenericValidator.isBlankOrNull(value)) {
try {
if (!value.equals(value2)) {
errors.add(field.getKey(),
Resources.getActionError(request, va, field));
return false;
}
} catch (Exception e) {
errors.add(field.getKey(),
Resources.getActionError(request, va, field));
return false;
}
}
return true;
}
Step 2: 编辑 validator-rules.xml ,加入"twofields" 规则。
method="validateTwoFields"
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.apache.struts.action.ActionErrors,
javax.servlet.http.HttpServletRequest"
depends="required" msg="errors.twofields">
function validateTwoFields(form) {
var bValid = true;
var focusField = null;
var i = 0;
var fields = new Array();
oTwoFields = new twofields();
for (x in oTwoFields) {
var field = form[oTwoFields[x][0]];
var secondField = form[oTwoFields[x][2]("secondProperty")];
if (field.type == 'text' ||
field.type == 'textarea' ||
field.type == 'select-one' ||
field.type == 'radio' ||
field.type == 'password') {
var value;
var secondValue;
// get field's value
if (field.type == "select-one") {
var si = field.selectedIndex;
value = field.options[si].value;
secondValue = secondField.options[si].value;
} else {
value = field.value;
secondValue = secondField.value;
}
if (value != secondValue) {
if (i == 0) {
focusField = field;
}
fields[i++] = oTwoFields[x][1];
bValid = false;
}
}
}
if (fields.length > 0) {
focusField.focus();
alert(fields.join('\n'));
}
return bValid;
}]]>
Step 3: 在validation.xml中为你的表单配置验证:
secondProperty
confirmPassword
这里errors.twofields的字段 '{0}'必须与字段'{1}' 的值相同。第三步的一个可选的工作就时使用 XDoclet 来生成validation.xml。requires (1) 配置XDoclet (当然)和(2) 在你的表单中添加添加一些@struts 标签setPassword方法。
/**
* Returns the password.
* @return String
*
* @struts.validator type="required" msgkey="errors.required"
* @struts.validator type="twofields" msgkey="errors.twofields"
* @struts.validator-args arg1resource="userForm.password"
* @struts.validator-args arg1resource="userForm.confirmPassword"
* @struts.validator-var name="secondProperty" value="confirmPassword"
*/
public String setPassword() {
return password;
}
我昨天已经把这个作为建议发送给struts-dev邮件列表, 但还没有收到任何消息。