复杂Struts Validation汇总

来源:互联网 发布:linux文件权限777 编辑:程序博客网 时间:2024/05/17 04:44

1.使用validwhen来做简单的交互性验证

这个网上经常可以搜到,也算是Validation里面最复杂、功能最花哨的一部分,单单为了实现这个功能,Validation就用了4个以上的类去工作,包括字符串的解析这种比较繁琐的工作。它的主要用途是用来做一些简单的交互性的验证,比如A不为空的时候B必须有值,可以做点简单的交互验证哦,但是我并不是很喜欢这东西,写表达式测试起来挺烦的,但是23个数据的相互制约还是可以考虑,最常用的就是上面说到的要求,我们可以这样写:

<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" onsubmit="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就会默认的包含一个namevalidator,增加一点配置的重用性。在系统启动的时候,validation会检查所有formparent并进行合并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">

由于项目中使用的是LazyValidatorFormform表单里的名字是以map方式传回Action,并支持POJOpopulate,所以我们的验证器会有对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里面针对没一个formvalidator集合,实际是存放在ValidatorResources中的org.apache.commons.validator.Form中的一个List类型的多个org.apache.commons.validator.Field。然后我们就可以任意的去修改它们,发挥你的聪明才智去适应你的需求吧!记得在修改完毕以后需要完璧归赵:ValidatorResources资源被存放在ServletContext里面作为全局变量保存,你在修改完Form里面的lFields以后需要重新放回去,这个很关键,否则你修改的东西就只能生效一次,下次再来的时候人家就不认识你咯。

至于如何修改我就不给出代码了,有兴趣的朋友可以看看附件,代码我简单改过,主要是去掉了公司的信息,代码还在测试使用中(因为要适应我们项目的需要),也许你会感到代码写的比较ugly,狂多的for循环和if else,写的时候着实耗费我不少功夫去调试

 

原创粉丝点击