1.使用validwhen来做简单的交互性验证
这个网上经常可以搜到,也算是Validation里面最复杂、功能最花哨的一部分,单单为了实现这个功能,Validation就用了4个以上的类去工作,包括字符串的解析这种比较繁琐的工作。它的主要用途是用来做一些简单的交互性的验证,比如A不为空的时候B必须有值,可以做点简单的交互验证哦,但是我并不是很喜欢这东西,写表达式测试起来挺烦的,但是2,3个数据的相互制约还是可以考虑,最常用的就是上面说到的要求,我们可以这样写:
<field property="name" depends="validwhen">
<arg position="0" key="label.name" resource="true" bundle="common" />
<var>
<var-name>test</var-name>
<var-value>((age == null) or (*this* != null))</var-value>
</var>
</field>
age不为空的时候name也不能为空,很常见的验证要求。由于这东西每次调试都要重启Server,很烦人,所以我也没做过多的尝试,那个表达式写起来也挺折腾人的,一不小心就失败。如果有兴趣可以参考Validation官方文档中的几条要求做更复杂的表达式来给你的表单做验证。当然这东西还是问题多多的,也可以说凡是用配置文件搞出来的东西,就不会百分之百的符合你的要求。
2.使用indexedListProperty为一个循环fields
这也是Validation里面默认就提供的功能,如果你的表单中包含循环元素生成的元素的时候就可以考虑用indexedListProperty来进行验证,这个很简单,只给出例子:
<field property="name" indexedListProperty="friends" depends="required">
<arg position="0" key="label.name" resource="true" bundle="common" />
</field>
indexedListProperty经常与validwhen一起用。
3.自定义validator
Validation里面提供的都是写基本的验证,更多时候需要自定义一些验证来整合我们的业务,所以validation提供了它的扩展接口,其实也就是用配置文件validator-rules.xml的方式来扩展validator。有了这个东西,你可以把任意复杂度的东西放在里面做你想做的验证。这个扩展功能很得人心,这样Validation的灵活度就非常大,更好的做到重用。
<validator name="name"
classname="com.cyan.action.validator.CommonValidator"
method="validateName"
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.apache.struts.action.ActionMessages,
org.apache.commons.validator.Validator,
javax.servlet.http.HttpServletRequest"
msg="msg.error.name">
</validator>
指定了这个新的validator,你可以任意加入自己的验证机制,在com.cyan.action.validator.CommonValidator中增加validateName()方法,注意在配置文件中已经默认指定了适合Struts ActionForm所需要的参数,你也需要在方法里使用,这样才能整合进Struts Validation并且方便的使用它的Message popup机制。这样你就可以实现你自己的validator:
public static boolean validateName(Object bean, ValidatorAction va,
Field field, ActionMessages errors, Validator validator,
HttpServletRequest request) {
String value = null;
Integer result = null;
value = evaluateBean(bean, field);
// add your validation logic here
if (!GenericValidator.isBlankOrNull(value)) {
errors.add(field.getKey(),
Resources.getActionMessage(validator, request, va, field));
return false;
}
return true;
}
?这样的扩展写着非常的自如,因为本身Validation对这个CommonValidator的要求很低,它不需要继承和实现任何接口类,当然你也可以继承org.apache.struts.validator.FieldChecks去继承它的一些基本验证。你也同样可以修改validator-rules.xml里面的默认validator指定到你自己的验证规则上。
4.JavaScript客户端验证
这个功能很早就在Struts中得到了支持,因为是客户端就验证了,可以有效减少服务器端的负载,而且Struts Validation JavaScript模式还可以提供Client Server两端验证这种双保险机制,可以减少不少工作量和维护工作。要使用它除了在服务器端进行基础的validation配置以外,还需要给你的页面form增加一点东西:
< script language="Javascript1.1" src="js/validator.js">< /script>
< html:javascript formName=”loginForm”>
< html:form action="manageContract.do" οnsubmit="return validateLoginForm(this);>
大概是这么写,显然这不是我想说的,网上找这个很容易。我要说的是JavaScript验证方式的问题: JavaScript方式的验证会自动将后台验证代码转化成JavaScript,毕竟是框架生成的,会导致每页JS代码暴增,会影响页面加载速度;还有一个问题就是你自己定义的验证人家没法给你转化成JS,怎么办?用它的JavaScript Plugin。
5.验证器的继承
上面已经提到过,系统对验证的要求比较高,common validation就多达100多个,那么按照validation默认的方式对每个ActionForm去写这重复的东西很不现实,所以Validation提供了一个继承方式:
<form name="BaseForm">
<field property="name" depends="required">
<arg position="0" key="label.name" resource="true" bundle="common" />
</field>
</form>
<form name="loginForm" extends="BaseForm">
</form>
这样loginForm就会默认的包含一个name的validator,增加一点配置的重用性。在系统启动的时候,validation会检查所有form的parent并进行合并validator,它会让“子类”form里面的validator覆盖父类的,也就是说你继承了BaseForm,如果有不同的验证,你可以把它覆盖掉。
6.扩展Validation
这里说的扩展Validation不是说写那个validator-rules.xml,我要说的是Validation验证还是太简单,我想更好的使用它就得在它的基础上进行扩展。用框架的时候为了使得框架更适合我们的项目,自然而然的选择去扩展,在Struts1中我们最经常看到的就是扩展org.apache.struts.action.RequestProcessor中的方法,比如其提供的
protected boolean processPreprocess(HttpServletRequest request,
HttpServletResponse response) {
return (true);
}
这是Struts框架中提供的一个很常用的扩展点,不过自打有了AOP之后它有点落寞,不妨碍我继续数用 :P。
之所以要扩展,问题就来自于上面的使用validation继承,这个继承的问题在于你的子类form会被强迫加入所有父类的validator,比如父类里面有一个age的验证,而你子类里面也被强制增加了这个age验证器,不管你有没有age这个字段。我不知道是validation没想明白还是我没用明白,这样的继承机制谁还敢用,所以我使用下面的扩展方案,如果有其他办法,希望不吝赐教。
在这里先抱怨一下,Validation的确是个好东西,但是它的代码显然是积怨太深,每次验证出问题我去调试跟代码都会很痛苦,因为它写的的确很难懂,好多类、好多方法掺和在一起,也许是我无法理解那些大师们的用意。
扩展org.apache.struts.validator.ValidatorForm中的validate()方法,当然你的类是可以继承任意ValidatorForm的子类,我们继承的是LazyValidatorForm。
这个扩展类里面的代码就比较多了(主要因为里面bug比较多 ),这里只给出部分代码段,主要实现的功能有:
1.子类实现“伪”继承,就是说子类只根据需要取父类的validator
2.validator支持多个名字,如:<field property="name, person.name" depends="required, length">
由于项目中使用的是LazyValidatorForm,form表单里的名字是以map方式传回Action,并支持POJO的populate,所以我们的验证器会有对person.name这种字段的验证,当然扩展的时候也主要在这里出现问题。
首先是修改validator加载的来源:
public class BaseForm extends LazyValidatorForm {
public ActionErrors validate(ActionMapping mapping,
HttpServletRequest request) {
ServletContext application = getServlet().getServletContext();
ActionErrors errors = new ActionErrors();
String validationKey = getValidationKey(mapping, request);
// Override code Validator validator =
//Resources.initValidator(validationKey, this, application, request,? errors, page);
Validator validator =
initValidator(validationKey, this, application, request,
errors, page);
try {
validatorResults = validator.validate();
} catch (ValidatorException e) {
logger.error(e.getMessage(), e);
}
return errors;
}
}
顺便你还得提供一个类似于org.apache.struts.validator.Resources.initValidator()的方法:
public Validator initValidator(String key, Object bean,
ServletContext application, HttpServletRequest request,
ActionMessages errors, int page) {
ValidatorResources resources =
Resources.getValidatorResources(application, request);
Locale locale = RequestUtils.getUserLocale(request, null);
Form form = resources.getForm(locale, key);
// Override codes here start
if (form != null) {
List lFields = (List) getPrivateField(form, "lFields");
Map hFields = (Map) getPrivateField(form, "hFields");
filterFields(lFields, resources, hFields);
}
// end
Validator validator = new Validator(resources, key);
validator.setUseContextClassLoader(true);
validator.setPage(page);
validator.setParameter(SERVLET_CONTEXT_PARAM, application);
validator.setParameter(HTTP_SERVLET_REQUEST_PARAM, request);
validator.setParameter(Validator.LOCALE_PARAM, locale);
validator.setParameter(ACTION_MESSAGES_PARAM, errors);
validator.setParameter(Validator.BEAN_PARAM, bean);
return validator;
}
因为这并不是框架中提供的一个扩展点,所以这个代码看起来挺别扭,明明只要修改它一句话,但其他的你也得加上。
这样你就可以修改它加载validator的方式,不过这只是个开始,validation并不希望你去这么做,真正的麻烦还在后面。其实麻烦已经出现了,在上面这段覆盖initValidator()的方法中我已经被迫使用了Java reflection中的private property accessor,再一次提醒你人家validation不想你去碰它,god bless me。
这样总算是获得了Struts Validation里面针对没一个form的validator集合,实际是存放在ValidatorResources中的org.apache.commons.validator.Form中的一个List类型的多个org.apache.commons.validator.Field。然后我们就可以任意的去修改它们,发挥你的聪明才智去适应你的需求吧!记得在修改完毕以后需要完璧归赵:ValidatorResources资源被存放在ServletContext里面作为全局变量保存,你在修改完Form里面的lFields以后需要重新放回去,这个很关键,否则你修改的东西就只能生效一次,下次再来的时候人家就不认识你咯。
至于如何修改我就不给出代码了,有兴趣的朋友可以看看附件,代码我简单改过,主要是去掉了公司的信息,代码还在测试使用中(因为要适应我们项目的需要),也许你会感到代码写的比较ugly,狂多的for循环和if else,写的时候着实耗费我不少功夫去调试