`
包子_feiFEI
  • 浏览: 70802 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类

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"是不变的。至于其他细节不在这里详细叙述。

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

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

分享到:
评论
1 楼 qq29oo 2016-07-14  

相关推荐

Global site tag (gtag.js) - Google Analytics