`
yueguangyuan
  • 浏览: 338901 次
  • 性别: Icon_minigender_1
  • 来自: 新加坡
社区版块
存档分类
最新评论

复杂Struts Validation汇总

阅读更多

不得不承认我用了太久的Struts1,但实际上并没有太多好的回忆,在大家都忙着学习Seam、JSF、SpringMVC和Struts2的时候我还不得不使用老掉牙的Struts1。好在每次用还是会用新收获,前提是你跳槽到新公司 -__-!!

    背景:说正题,新公司新项目,主体不在Web App上面,因为系统主要用于数据管理对数据库的完整性和准确性,所以需求上光是验证就有60页的文档,对每一条进入系统的数据都有很严格的要求:包括单个数据和数据之间的关系都有详细的规定,由于我负责的是单个数据的验证,所以本文只关心单个数据的有效性验证。

   

    方案:既然用了Struts,那就很难离开ActionForm(当然也有狠人直接把ActionFrom也干掉@_@),用了ActionForm自然而然的选择Struts Validation。Struts Validation不知道算不算目前Web表现层最好的验证方案,但是肯定是使用最广泛的一种,但是它仍然不够用。

 

    又跑题了,所以对于单个用户录入数据的有效性验证仍然采用Struts Validation,在做过一些改动后,Struts Validation还是能很好的完成这项工作。

 

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增加一点东西(参考:http://www.xker.com/page/e2007/0727/28232.html):

< 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。

//TODO

 

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;
    }

 (代码中的private property accessor参考:http://snippets.dzone.com/posts/show/2242

 

因为这并不是框架中提供的一个扩展点,所以这个代码看起来挺别扭,明明只要修改它一句话,但其他的你也得加上。

这样你就可以修改它加载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,写的时候着实耗费我不少功夫去调试。

 

这样就算是把我目前知道的有关Struts Validation的一些复杂应用总结完了,其实验证就是一个纯体力活,写这些东西就是为了减少奋战在一线的战友们减轻点负担,hope it will help you。

分享到:
评论
2 楼 dmwdmc 2010-05-04  
小月。。。。。。。。。
1 楼 xyjava 2008-11-17  
沙发,,,

相关推荐

    java面试问题汇总(非常全面)

    Struts-validation的2种方法 - **基于 ActionForm**:通过在 `ActionForm` 类中重写 `validate()` 方法来验证数据。 - **基于拦截器**:使用 Struts2 的拦截器进行数据验证。 #### 30. JavaScript起到什么样的...

    pimpinella_3cd_01_0716.pdf

    pimpinella_3cd_01_0716

    FIB English learning

    FIB English learning

    linux下 jq 截取json文件信息

    X86-jq安装包

    [AB PLC例程源码][MMS_046356]SELX.zip

    AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!

    大圣挪车小程序1.3.5+前端.zip

    大圣挪车小程序1.3.5 前端

    Manus.im 产品及开发团队研究报告.pdf

    Manus.im 产品及开发团队研究报告.pdf

    [AB PLC例程源码][MMS_044663]Control daisy chain wiring in Fieldbus Foundation.zip

    AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!

    sun_3ck_01a_0918.pdf

    sun_3ck_01a_0918

    支持适用于PERC H330/H730/H730P/H830/H730P系列RAID卡MX/FD33xD/FD33xS控制器的驱动安装指南

    下载 1. 单击“立即下载”,以下载该文件。 2. 出现“文件下载”窗口后,单击“保存”,以将文件保存到硬盘。 安装 1. 浏览至文件下载目标位置并双击新下载的文件。 2. 仔细阅读对话窗口中显示的发布信息。 3. 下载并安装对话窗口中标识的任何必备项,然后再继续。 4. 单击“Install”(安装)按钮。 5. 按照其余提示执行更新。 安装 1. 将解压的文件复制到可访问Windows的介质。 2. 将系统重新引导至Windows操作系统。 3. 打开“服务器管理器”->“设备管理器”->“存储控制器”,然后单击“PERC控制器”。 5. 单击“更新驱动程序软件”,并按照提示更新驱动程序。 4. 重新引导系统以使更改生效。

    硬盘安装器,支持硬盘安装,无需制作U盘PE!

    支持所有操作系统一键安装。

    matlab程序代码项目案例:使用 Simulink 进行自适应 MPC 设计

    matlab程序代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!

    [AB PLC例程源码][MMS_044098]1769-ASCII Simultaneous Mode.zip

    AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!

    swanson_01_1106.pdf

    swanson_01_1106

    [AB PLC例程源码][MMS_047811]SAF1 - Store.zip

    AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!

    [AB PLC例程源码][MMS_043879]Programming in SFC and ST Language.zip

    AB PLC例程代码项目案例 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我!

    sun_3ck_01_0919.pdf

    sun_3ck_01_0919

    方言距离数据.岭南学院产业与区域经济研究中心

    各城市方言距离数据-中山大学岭南学院产业与区域经济研究中心 方言距离是指两种或多种方言之间的相似程度或差异程度。参考中山大学岭南学院产业与区域经济研究中心的刘毓芸等(2015)文献。他们基于方言树图,并参考《汉语方言大词典》和《中国语言地图集》对方言的划分,将汉语方言从宽泛到具体分为以下几个层级:汉语→方言大区→方言区→方言片。为了量化县与县之间的方言差异,他们采用了一种赋值方法: 若它们分属不同方言大区,则距离为3。: 若两个县同属一个方言片,则它们之间的方言距离为0; 若两个县属于同一方言区但不同方言片,则距离为1; 若它们属于同一方言大区但不同方言区,则距离为2; 方言距离是一个反映方言之间相似程度或差异程度的重要指标,它在语音识别、方言研究等领域具有广泛的应用价值。 参考文献:[1]刘毓芸, 徐现祥, 肖泽凯. 2015. 劳动力跨方言流动的倒U型模式[J]. 经济研究, 50(10): 134-146+162. 指标 语系、语族、方言大区、方言区/语支、方言片/语种、Supergroup、Dialect、group、Sub-dialect、groupPref_1、Pref_2、DiaDist、PrefCode_1、PrefCode_2等等。

Global site tag (gtag.js) - Google Analytics