struts2 validator 详细小结

前面讲到了一种验证方法,是在Action的 validate方法中通过代码的方式来完成的。而struts2提供了另外一种 方式来实现输入验证。

这种方式就是使用validate框架来实现输入校验,这种方式是基于XML的验证。

文件名为XXXAction-validation.xml

那么校验xml文件格式该如何写呢?

可以使用firefox查看此xml的DTD定义,地址为 http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd

在此列出此DTD的内容

<?xml version="1.0" encoding="UTF-8"?>

<!-- 
  XWork Validators DTD. 
  Used the following DOCTYPE. 
  <!DOCTYPE validators PUBLIC 
          "-//OpenSymphony Group//XWork Validator 1.0.2//EN" 
          "
http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> 
-->

<!ELEMENT validators (field|validator)+>

<!ELEMENT field (field-validator+)> 
<!ATTLIST field 
    name CDATA #REQUIRED 
>

<!ELEMENT field-validator (param*, message)> 
<!ATTLIST field-validator 
    type CDATA #REQUIRED 
    short-circuit (true|false) "false" 
>

<!ELEMENT validator (param*, message)> 
<!ATTLIST validator 
    type CDATA #REQUIRED 
    short-circuit (true|false) "false" 
>

<!ELEMENT param (#PCDATA)> 
<!ATTLIST param 
    name CDATA #REQUIRED 
>

<!ELEMENT message (#PCDATA)> 
<!ATTLIST message 
    key CDATA #IMPLIED 
>

由此DTD的定义可知,此XML文件的根元素为validators。

在根元素下可以有若干个field或validator子元素,即分别代表着字段校验和非字段校验,它们的区别将在后面介绍。


字段校验

字段校验代表着field,标签有个name属性石必填的,它和表单中的name属性值是一样的。 
这里我填入username。

<validators> 
    <field name="username"> 
    </field> 
</validators>

field下面有个子元素叫field-validator,代表着要用什么方式来进行校验,其有个属性叫 type,也是必填的。


<validators> 
    <field name="username"> 
        <field-validator type=""> 
        </field-validator> 
    </field> 
</validators>

type应该填入什么内容呢?

可以查看xwork的源文件,在包 com.opensymphony.xwork2.validator.validators下有个文件default.xml,在这个文件中定义了 type属性值。

    <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/> 
    <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>

还有很多定义好的type值,这里不一一列举

在validator元素中,name属性表示可以定义的type值,class 表示用哪个类来进行校验,这是struts2默认设置好的校验器,我们可以直接使用。

在这个例子中,我们将type设置为requiredstring

<validators> 
    <field name="username"> 
        <field-validator type="requiredstring"> 
        </field-validator> 
    </field> 
</validators>

在field-validator下面有两个子元素,param和message 
param可以任意个,可有可无,但是message有且只有一个。

param表示传入的参数,message表示出错时显示的信息。也就是说message可以是任意的字符串,但是param却是特定的。

在继续之前还是看看 com.opensymphony.xwork2.validator.validators.RequiredStringValidator这个类的源代码。首先确定已经下载了源代码,并且在MyEclipse中关联了源代码,这是一个好习惯。

在这个类中有一个成员变量

private boolean doTrim = true;

因此,我们在param中要做的就是,将param元素的name属性设置为doTrim,值为true。

<validators> 
    <field name="username"> 
        <field-validator type="requiredstring"> 
            <param name="trim"<true>/param> 
            <message<username should not be blank!</message> 
        </field-validator> 
    </field> 
</validators>

trim表示是否忽略空格,默认是true,因此在此也可以省略掉param元素。

其实在 field-validator元素中还有一个属性short-circuit,其默认值是false,表示的意思是短路,即前面验证失败,后面就不做验证了。

然而这仅仅只是一个校验条件,还是以 username为例,在上次的例子中,还要求username的长度要在6到10之间,因此,继续看default.xml文件

在其中找到这样一个元素 
<validator name="stringlength" class="com.opensymphony.xwork2.validator.validators.StringLengthFieldValidator"/>

从字面上可以看出是限制字符串长度的,因此查看这个实现类

它有3个成员变量:

    private boolean doTrim = true; 
    private int maxLength = -1; 
    private int minLength = -1;

其中最主要的是maxLength和minLength分别代表最大长度和最小长度

因此在这个校验器中内容应该如下:

    <field name="username"> 
        <field-validator type="stringlength"> 
            <param name="minLength">6</param> 
            <param name="maxLength">10&lr;/param> 
            <message>username should be between ${minLength} and ${maxLength}</message> 
        </field-validator> 
    </field>

有个问题就是 
    <validator name="required" class="com.opensymphony.xwork2.validator.validators.RequiredFieldValidator"/> 
    <validator name="requiredstring" class="com.opensymphony.xwork2.validator.validators.RequiredStringValidator"/>

required和requiredstring有什么区别吗?

requiredstring是指这个字符串是必须的,而required表示这个字段是必须的,而没有指明这个字段是否必须是字符串。

因此对于字符串,可以使用requiredstring,而对于非字符串类型就必须使用required了。如在这个示例中的age,birthday。注意birthday是Date类型的,不是String。

还是回顾一下register2.jsp的页面内容。

body中的内容为: 
<s:actionerror /> 
    <s:form action="register2"  theme="simple"> 
                    …… 
                    …… 
    </s:form> 
但是输入数据提交后,仍然显示的是以前的出错信息,并不是今天所设置的信息。

这就涉及到一些知识点了,因为校验框架产生的错误是fielderror,不会在actionerror中显示。

因此,需要将 theme="simple"去掉,并把那些表格标签页去掉,然后执行时会看到在username上方的错误信息。

还有一个问题就是validate校验框架和Action中validate方法是否冲突。

实际上虽然会使用框架验证,但是也会调用Action的validate方法,通过上面的显示结果应该可以清楚的看到,在表单上面集中的显示了actionerror。

但是如果把Action中的validate方法的出错信息add到field中会有什么效果呢??

修改RegisterAction中validate方法,将 addActionError改为addFieldError。

当没有输入用户名时,会显示如下错误信息:

username should not be blank! 
username invalid

为什么会显示这样的结果呢???

首先肯定的是在validate中增加的fielderror,和xml中不会冲突

即错误信息不会被覆盖,而是两者都有,而且先显示xml中定义的错误信息,然后才是validate中定义的错误信息。实际上是所有的xml执行完毕后,在执行validate。

为什么会产生这样的效果呢???那么你就必须知道fielderror到底是什么!继续查看源代码。

因为RegisterAction是继承的 ActionSupport,addFieldError是继承自ActionSupport,所以先看看ActionSupport中 addFieldError的实现方法。

在 ActionSupport中addFieldError是这么实现的,

    public void addActionError(String anErrorMessage) { 
        validationAware.addActionError(anErrorMessage); 
    } 
即通过调用validationAware对象的addActionError,而validationAware是 ValidationAwareSupport的一个实例,在ValidationAwareSupport中定义了fielderror是什么。

    private Map>String, List<String>> fieldErrors;

    public synchronized void addFieldError(String fieldName, String errorMessage) { 
        final Map<String, List<String>> errors = internalGetFieldErrors(); 
        List<String> thisFieldErrors = errors.get(fieldName);

        if (thisFieldErrors == null) { 
            thisFieldErrors = new ArrayList<String>(); 
            errors.put(fieldName, thisFieldErrors); 
        }

        thisFieldErrors.add(errorMessage); 
    }

通过源代码,可以看到fieldErrors实际上是一个Map>String, List<String>>。key是String类型的,而value是List<String>

而实际上这个List是通过ArrayList<String>来实现的,也就是说,key是String类型的,而value是ArrayList<String>。

虽然一个key只能对应一个value,但是在这里value并不是一个字符串,而是一个数组。所以错误信息不会被覆盖掉。

在ActionSupport类中存在一个方法getFieldErrors,按照方法名可以猜的出该方法返回的是fieldError这个数组,既然如此那么是否可以通过 getFieldErrors直接添加呢??

    List<String> list = new ArrayList<String>(); 
    list.add("username should be between 6 and 10"); 
    this.getFieldErrors().put("username",list);

虽然编译器没有报错,但是实际上是不行的。查看API文档:

getFieldErrors,其解释如下:

public Map<String,List<String>> getFieldErrors()

Description copied from interface: ValidationAware

Get the field specific errors associated with this action. Error messages should not be added directly here, as implementations are free to return a new Collection or an

Unmodifiable Collection.

注释:

这个方法返回与这个action相关的具体的fielderrors,错误信息不能直接从这里添加,执行结果返回一个新的集合或一个不可修改的集合

这是什么意思呢?看源代码,这个方法同样是在 ValidationAwareSupport中实现的。

    public synchronized Map<String, List<String>> getFieldErrors() { 
        return new LinkedHashMap<String, List<String>>(internalGetFieldErrors()); 
    } 
由此可以看到该方法返回的是一个新的LinkedHashMap,只是一个拷贝,而不是原集合,所以这样直接添加是无法显示出来的。

那么何时使用validate验证框架,什么时候使用action中的 validate方法呢?

一般来说,简单验证可以使用 xml,复杂时用validate

前面讲到了 struts2的数据校验,那么为什么要有服务器校验??拥有了客户端校验不是也行吗??

服务端校验是必须的,即使有客户端校验。因为可以不通过browser访问web服务器!!强健的web应用要有客户端和服务器端的验证。

当然,struts2同样支持客户端验证。

要使用struts2的客户端校验,必须满足一下条件:

1.form的主题(theme)一定不能设置为simple

2.将struts2 form标签中validate属性设置为true

但是看到小时效果后就会发现,struts2生成的也是js代码,但是效果却很差,所以一般来说,使用struts2的服务器端校验,而客户端校验自己写。

struts2标签支持事件,可以像使用html标签一样使用。

同样用validate校验框架也应该可以使用局部校验:

当action中有多个方法时,需要在和action同目录下新建文件 XXXAction-XXX(方法名)-validation.xml

在实例化子类对象时,会先执行父类的全局校验,然后是局部校验,接着是子类的全局校验,然后是子类的局部校验,因此不要提供全局校验。 

字段校验和非字段校验的区别

通俗点讲:

字段校验:校验谁,用什么方法

非字段校验:用什么校验,校验谁

非字段校验示例:

<validator type="requiredstring"> 
    <param name="fieldName">username</param> 
    <message></message> 
</validator>

其中tyoe="fieldName"是不变的。至于其他细节不在这里详细叙述。

但还是建议使用字段校验器。

通过今天的学习,了解到了另外一种通过配置文件进行数据校验的方法。而这种方法显得更加简单,易懂。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值