Struts Validation框架浅尝

from: http://raibledesigns.com/downloads/appfuse/api/org/appfuse/webapp/util/ValidationUtil.html
 
前言

       早就听说Struts1.1Validate框架的种种好处,这次借着项目采用了Struts,简单的研究了一下它的用法。在这个过程中体会到了它的好处,但是它也存在着一些小小的问题。在此一并写出与大伙分享。作者使用的环境是Struts1.1

配置Struts Validation框架

       相信正在阅读本文的读者都是对struts有一定经验的,对于struts到哪去下载、如何安装配置就不必笔者多言。此处仅仅只讨论struts validation框架的安装和配置。通常,我们有2种方法来进行这个过程。

-          方法一:

struts自带的webapps目录下解压struts-balnk.war到项目的web工程目录。这是最简单创建struts工程的方式,在这个工程中就已经包含了一个struts应用的所有需要的开发包和这些包的基本配置,自然也包含了其中的validation框架。它非常适合在项目的初创阶段,我们所需做的就是在这个空白工程中填点什么就可以了。

-          方法二:

对于已经存在的struts工程,如何添加valiadation框架支持所需的步骤也非常简单。

1.       首先在struts-config.xml中添加以下内容

<plug-in className="org.apache.struts.validator.ValidatorPlugIn">

<set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml" />

</plug-in>

2.       然后在web-inf目录中创建2个文件validator-rules.xmlvalidation.xml。(这2个文件也可从struts-balnk.war中获取)。

3.       复制validation框架所需要的类包到web-inf/lib下,这些类包是:common-validator.jar

这样,我们就可以使用strutsvalidation框架了。

使用validation框架

       花了这么多工夫作准备就是为了使用它,在这个部分我们来看看它是如何使用的。在struts1.1出现以前,验证是在formbeanvalidate方法和actionvalidate方法中进行。在formbean处,我们进行的主要是界面输入的有效性判断,而在action处,主要是业务逻辑的有效性判断。而在1.1提出的validation框架,个人觉得它更着重于formbean的有效性判断。

       Struts的创建者为什么会在struts1.1中提出一个validation框架呢?关于这方面的回答和资料网上已经有太多,归结起来主要就是验证方法的复用、维护、以及开发的效率。在解释之前,通过一个简单的例子来了解validation框架是如何在strtuts中使用的。

问题

       这是一个简单的用户管理程序,使用者可以对用户信息进行相应的CRUD操作(增查改删)。其中用户信息包含用户名称、用户地址、用户描述、用户密码。对于以上的各种操作,约束如下:

-          增加和修改:用户名称、用户地址、用户密码、用户密码确认为必填项,同时密码和确认密码2个域的值必须一致。

-          删除和查询:用户名称为必填项。

解决和使用步骤

       在此,笔者列出struts1.1和之前版本的异同部分,方便读者进行比较和判断。

1.       创建struts项目的基本环境。

2        1.1中添加了validation框架的支持,参见上一节的做法。

2.       创建相应的页面,确定页面流程(page flow)。

3.       创建相应的formbean

2        strut1.1版本解决办法:

为了简单起见使用DynaActionForm。又由于使用了validation框架,此处使用DynaValidatorFormstruts-config.xm的内容:

<form-bean name="editForm" type="org.apache.struts.validator.DynaValidatorForm">

<form-property name="id" type="java.lang.Integer"/>

<form-property name="name" type="java.lang.String"/>

     <form-property name="pwd1" type="java.lang.String"/>

     <form-property name="pwd2" type="java.lang.String"/>

<form-property name="address" type="java.lang.String"/>

<form-property name="desc" type="java.lang.String"/>

</form-bean>

然后,修改web-inf目录下的validation.xml文件内容,其中form的名字和各域的名字必须和struts-config.xml中一致:

<form name="/editForm ">

     <field property="name" depends="required">

          <arg0 key="editForm.name"/>

     </field>

     <field property="pwd1" depends="required,mask">

          <arg0 key="editForm.password"/>

          <var>

                <var-name>mask</var-name>

                <var-value>^[0-9a-zA-Z]*$</var-value>

          </var>

     </field>

     <field property="pwd2" depends="required,mask">

          <arg0 key="editForm.password"/>

          <var>

                <var-name>mask</var-name>

                <var-value>^[0-9a-zA-Z]*$</var-value>

          </var>

     </field>

<field property="address" depends="required ">

          <arg0 key="editForm.address"/>

     </field>

</form>

2        1.1之前的解决办法

老老实实地写ActionForm的子类,覆盖validate方法。

4.       创建相应的Action,同时Actionvalidate属性置为true,以启用validation框架。

5.       书写ApplicationResources.properties,添加错误信息。

2        struts1.1

errors.required={0} is required.

errors.minlength={0} can not be less than {1} characters.

errors.maxlength={0} can not be greater than {1} characters.

errors.invalid={0} is invalid.

 

errors.byte={0} must be a byte.

errors.short={0} must be a short.

errors.integer={0} must be an integer.

errors.long={0} must be a long.

errors.float={0} must be a float.

errors.double={0} must be a double.

 

errors.date={0} is not a date.

errors.range={0} is not in the range {1} through {2}.

errors.creditcard={0} is an invalid credit card number.

errors.email={0} is an invalid e-mail address.

 

editForm.name= name.

editForm.password= password.

editForm.address= address

2        1.1之前的解决方法,与之类似。

6.       在相应的jsp中添加<html:errors/>

从整个使用过程来看,就验证部分而言,使用struts1.1的开发效率大大的得到了提高,而且通过validator-rules.xml使验证方法能被不同的formbean复用,而且可维护性也大大的得到提高。通过mask,使得单个页面域的验证非常灵活。

但是细心的读者可能也发现了这个验证文件仅仅指明2password域是必填的,但并没有满足他们必须是相等的这种情形的判断,对于这一点,我们可使用自定义的validator并将它添加到validator-rules.xml文件中来完成。

创建自定义的validator

       对于validator的创建,可以归结为3步:

1.       创建validator类,validator必须包含一个以valid开始的方法,并且它的函数签名必须如下:

validXXX(java.lang.Object,

org.apache.commons.validator.ValidatorAction,

org.apache.commons.validator.Field,  org.apache.struts.action.ActionErrors,

javax.servlet.http.HttpServletRequest,  javax.servlet.ServletContext)

在本例中,自定义的Validator如下(摘至Struts提供的例子,用来提供2个域的相等性检查):

package com.drc.util;

import org.apache.commons.validator.ValidatorUtil;
import org.apache.commons.validator.Field;
import org.apache.commons.validator.ValidatorAction;
import org.apache.struts.action.ActionErrors;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.validator.GenericValidator;
import org.apache.struts.validator.Resources;


public class ValidationUtil {

    public ValidationUtil() {
        super();
    }

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

}

2.       添加到validator-rules.xml文件中:

<validator name="twofields"
    classname="com.drc.util.ValidationUtil" 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">
    <javascript><![CDATA[
        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;
        }]]></javascript>
</validator>

                   在对应的属性文件中添加对应的消息(errors.twofiled----errors.twofields = {0} is not match with {1}.)。

3.       validation.xml中使用:

            <field property="userPassword1" depends="required,mask,minlength,twofields">
                <arg0 key="registerForm.userPassword.displayPassword" />
                <arg1 name="minlength" key="${var:minlength}" resource="false" />
                <arg1 name="twofields" key="registerForm.userPassword.different" />
                <var>
                 <var-name>minlength</var-name>
                 <var-value>5</var-value>
                </var>
             <var>
                    <var-name>mask</var-name>
                    <var-value>^[0-9a-zA-Z]*$</var-value>
                </var>
                <var>
                    <var-name>secondProperty</var-name>
                    <var-value>userPassword2</var-value>
                </var>
            </field>

         通过自定义validatorvalidation框架的可扩展性大大的得到提高。而且也使得不同的验证方法能够很好的得到复用

javascript支持

       web应用中,使用javascript的机会非常多。虽然不少书上提及客户有可能从浏览器关闭js的执行,但是要想完全的不使用它,目前看来好像还不行。比如一些复杂的UI是必须通过js来实现的(如下拉式菜单等等)。那么validation框架支不支持客户端的js验证呢?

答案是:当然。具体做法是:

-          在页面html:form标签内部添加onsubmit="return validateEditForm(this);"(具体的语法:validate+validation.xml文件中定义的form的名字);如:

<html:form action="registerAction.do" method="post" onsubmit="return validateRegisterActionForm(this);">

-          html:form内部块中添加:<html:javascript formName="registerActionForm"/>

       如此2步即可。虽然,validation框架非常简单易用,但是还是有需要注意的地方。

使用注意

1.       使用validation框架后,form必须从ValidatorForm中派生,同时必须在你的validate方法中先调用基类的validate方法。对于使用Dyna开头的方法来创建formbean的读者,你也必须改为以dyna开头含有validatorform

2.       注意DynaValidatorFormValidatorForm)和DynaValidatorActionFormValidatorActionForm)的区别。刚开始时从帮助中没看明白这2者的区别,后来从网上一篇文章中得到了用法的区别。前者主要的视角是formbean,而后者的视角是action

formbean被不同的action使用时,对于不同的action而言,使用的formbean的属性集合有大有小。此时如果仍然以formbean为主体,会造成其他action的不正常使用。因此,struts中提出了DynaValidatorActionFormValidatorActionForm)。此时在validation.xml中的form标签的name属性改为actionpath属性,又由于action中有attributename属性,validation框架就可根据这个action得到对应的formbean。例子:

<formset>

  <form name="/createAddress">

    <field property="city"

          depends="required">

      <arg0 key="prompt.city"/>

    </field>

  </form>

  <form name="/editAddress">

    <field property="state"

          depends="required">

      <arg0 key="prompt.state"/>

    </field>

  </form>

</formset>

3.       DispatchAction的配合。Struts1.1DispatchAction使得相关的Action的关系紧密,大大减少了应用中Action的个数,但是随之而来也带来了使用Validation框架的不便,不能不说是一个遗憾。读者也许认为这种情况可以使用第2条的解决方案来解决,即采用DyanValidatorActionForm,然后在Validation.xml文件中form的名称使用不同的Actiondpath,即在validation.xml中使用:<form name="/user.do?method=doAdd">    <form name="/user.do?method=doLoad">。然而,在目前的版本中Validation框架并不支持这种辨认。一种绕过这个情况的方法是,针对同一个Action实现类在Struts-config.xml文件中定义多个Actionpath,在不需要进行验证的地方将Actionvalidate属性置为false。即:

struts-config.xml

<action attribute="editForm" path="/user" name="editForm" input="/editUser.jsp"

parameter="method" scope="request" type="foxgem.struts.UserDispatchAction"

     validate="true">

               <forward name="load" path="/editUser.jsp"/>

               <forward name="action" path="/userquery.do?pageId=1"/>

</action>

       

<action attribute="editForm" path="/loaduser" name="editForm" input="/editUser.jsp"

          parameter="method" scope="request" type="foxgem.struts.UserDispatchAction"

          validate="false">

               <forward name="load" path="/editUser.jsp"/>

               <forward name="action" path="/userquery.do?pageId=1"/>

</action>

然后在validation.xml文件中使用2的方法。

结束语

       总的说来,validation框架大大的提高了页面验证的开发效率,更吸引人的是这些验证方法可通过自定义的validator来得到复用。使得这些验证代码更加集中,可维护性得到加强。当然随着项目的进行,validation.xmlvalidator-rules.xml会随之增长,这部分的维护工作加重了。

       同时,由于不能非常好的和DispatchAction一起协作,也使得大量使用DispatchAction的项目不能非常好的使用它。建议大量使用DispatchAction和页面验证非常复杂多变的项目可以暂时按原来的方法来验证,不使用validation框架。

       至于validation框架的其他详细信息,请参见struts的文档,在此不再赘述。

参考资料

n         http://raibledesigns.com/wiki/Wiki.jsp?page=SecuringDispatchAction DispatchActionvalidation框架的协作解决方案来源于此。

n         http://otn.oracle.com/oramag/oracle/04-jan/o14dev_struts.html Check Your Form with Validator

n         http://www-900.ibm.com/developerWorks/cn/java/l-struts1-1/ 深入Struts 1.1

n         http://javaboutique.internet.com/tutorials/Struts11Val/ Stepping through the Struts 1.1 Validator

posted @ 2004-12-16 13:05  wuxh  阅读(470)  评论(0)    收藏  举报