Struts Tag Library
对于一个MVC框架而言,重点是实现二个部分: 控制器部分和视图部分。 Struts2框架同样如此:控制器部分由
Action(以及隐藏的系列拦截器)来提供支持,而视图部分则通过大量标签来提供支持。
Struts2标签库使用OGNL表达式作为基础, 且默认支持OGNL, JSTL, Groovy和Velcity表达式。且把所有标签都
定义在URI为 /struts-tags 命名空间下,但我们依然可以依据功能将其分为三类:
1. UI标签;
a. 表单标签;
b. 非表单标签;树、Tab页等;
2. 非UI标签;
a. 流程控制: 条件、循环;
b. 数据访问: 输出ValueStack中的值;
3. AJAX标签;
Object Graph Navigation Language, 缩写为OGNL,是类似于EL的脚本语言,
一. 控制标签
Struts2中的非UI标签包括控制标签和数据标签,其中控制标签可以完成输出流程控制,例如条件、循环等操作,
也可完成对集合的合并、排序等操作,它有如下九个:
1) if: 选择输出;
2) elseIf/elseif: 与if标签结合使用;
3) else: 与if标签结合使用
4) append: 将多个集合拼接成一个新的集合;
5) generator: 将一个字符串解析成一个集合;
6) iterator: 将集合迭代输出;
7) merge: 将多个集合拼接成一个新集合,与append拼接方式不同;
8) sort: 对集合进行排序;
9) subset: 截取集合部分元素,形成新的子集;
1. iterator标签
对集合进行迭代,包括List,Set和数组,Map.
. value: 可选,被迭代的集合,通常使用OGNL表达式指定,如无,则使用ValueStack栈顶集合;
. id: 可选,指向集合中元素;
. status: 可选,指向迭代时IteratorStatus实例,通过该实例可判断当前迭代元素的属性;每次迭代都有
一个IteratorStatus实例,该实例包含以下方法:
a. int getCount(): 返回当前迭代了几个元素
b. int getIndex(): 返回当前迭代元素的索引
c. boolean isEven(): 当前索引是否为偶数
d. boolean isFirst(): 当前迭代元素是否是第一个元素
e. boolean isLast(): 当前迭代元素是否是最后一个元素
f. boolean isOdd(): 当前索引是否为奇数
2. if标签
根据一个Boolean表达式的值,来决定是否计算,输出标签体等内容。
3. append标签
将多个集合对象拼接起来,组成一个新的集合。
其中 id 属性确定拼接生成的新集合的名字。 append可接受多个<s:param ..../>子标签,每个子标签指定一个集合,append
将<s:param..../>指定的多个集合拼接成一个集合;
4. generator标签
将指定字符串按指定分隔符分隔成多个子串,临时生成的多个子串可以使用iterator标签来迭代输出。
a. count: 指定生成集合中元素总数;
b. separator: 指定解析字符串的分隔符;
c. val: 指定被解析的字符串;
d. converter: 可选,指定一个负责将集合中每个字符串转换成对象;
e. id: 指定访问集合的名称,如指定,则集合放在pageContext属性中;
5. merage标签
功能和append类似,但是新集合中各元素顺序不同:
append为:
1) 第一个集合第一个元素;
2) 第一个集合第二个元素;
3) 第一个集合第三个元素;
4) 第二个集合第一个元素;
5) 第二个集合第二个元素;
6) 第二个集合第三个元素;
7) 第三个集合第一个元素;
8) 第三个集合第二个元素;
9) 第三个集合第三个元素;
merage为:
1) 第一个集合第一个元素;
2) 第二个集合第一个元素;
3) 第三个集合第一个元素;
4) 第一个集合第二个元素;
5) 第二个集合第二个元素;
6) 第三个集合第二个元素;
7) 第一个集合第三个元素;
8) 第二个集合第三个元素;
9) 第三个集合第三个元素;
6. subset标签
取得集合的子集:
. count: 指定子集合元素个数;
. source: 指定源集合,如不指定,则默认取得ValueStack栈顶的集合;
. start: 指定从源集合中第几个元素开始截取,默契为第一个(值为0);
. decider: 指定由开发者是否选中该元素;
7. sort标签
对于指定的集合进行排序; 必须提供自己的排序规则,即提供实现 java.util.Comparator接口的实现类;
. comparator: 指定进行排序的 Comparator 实例;
. source: 指定被排序的集合,如不指定,则针对 ValueStack栈顶的集合;
二. 数据标签
提供各种数据访问相关的功能,包含显示一个Action中属性以及生成国际化输出等功能;
1. property标签
提供一种快速、方便地方式从ValueStack或ActionContext中获取值并显示;
. value: 指定输要输出的属性值,如没有指定,则默认输出ValueStack栈顶的值;
. default: 如输出属性值为 null, 则显示 default 属性的值;
. escape: 是否escape HTML 代码, 默认值为 true; 将 < 转化为 <
2. set 标签
将一个已有的值复制给新变量,然后放到指定的范围内
. name: 必填,新变量名字
. scope: 可选属性,指定新变量放置的范围,可以接受 application, session, request, page 或 action 五个值,
默认为 Action中;
. value: 可选,赋给变量的值,没指定,则为ValueStack栈顶的值;
. id: 可选,该元素的引用ID
3. push 标签
将某个值放到valueStack中,从而更加方便访问;
4. bean 标签
创建JavaBean实例;
. name: 必须,要实例化的JavaBean的完整类名;
. var: 可选,可通过该属性值在标签外访问已实例化的JavaBean;
5. Date 标签
格式化输出日期以及计算指定日期和当前时刻间的时差;
. format: 可选,指定日期显示格式;
. nice: 可选, 是否输出指定日期和当前时刻间的时差,默认值为 false;
. name: 必填,日期值;
. id: 可选,引用该元素的 id 值;
如果既指定nice="true", 又指定 format 属性,则会输出指定日期和当前时刻间的时差,即format属性失效;
如没有指定format属性,也没指定nice="true", 则系统会到资源文件中寻找 key 为 struts.date.format的消息作为格式,否
则采用默认的 DateFormat.MEDIUM格式输出。
6. action 标签
直接在JSP页面中直接调用Action
. id: 该Action的引用ID;
. name: 必须:调用哪个Action;
. namespace: 可选,调用Action所属的namespace;
. executeResult: 可选,是否将Action处理结果页面包含到本页面,默认值为false;
. ignoreContextParams: 可选,该页面中请求参数是否需传入调用的Action, 默认值为 false, 传入。
7. debug 标签
用于辅助调试,它在页面上生成一个超级链接,通过该链接可以查看到ValueStack和Stack Context中所有的值信息;
8. include 标签
将一个JSP或servlet包含到本页面中:
. value: 必填, 指定包含的资源;
. id: 可选,该标签的ID引用;
9. url 标签
生成一个URL 地址,可以通过为url标签指定 param 子元素,从而向指定URL发送请求参数。
. includeParams: 可选,是否包含请求参数,值为 none, get或者all.
. scheme: 设置schema属性;
. value: 可选,指定生成的URL值,如果无value有action就使用action指定Action作为URL地址;
如果均提供,则使用value指定URL值;如均不提供,则使用当前页面作为URL地址;
. action: 可选,指定action;
. namespace: 可选,指定命名空间;
. method: 可选,指定使用Action的方法;
. encode: 可选,是否需encode请求参数;
. includeContext: 是否需将当前上下文包含在URL地址中;
. anchor: 可选,指定URL锚点;
. id: 指定该URL元素的引用ID
10. i18n和text 标签
i18n用于显示指定指定语言资源文件;
. name: 资源文件名称;
text用于获取指定资源文件中指定key对应的值;
. name: 资源文件 key 值;
. id: 引用的标识;
三. Form标签
1. checkbox 标签
<s:set name="aBoolean" value="false"/><br>
<s:checkbox label="checkbox test" name="checkboxField1" value="#aBoolean" fieldValue="true"/>
<s:set name="aBoolean" value="true"/><br>
<s:checkbox label="checkbox test" name="checkboxField1" value="#aBoolean" fieldValue="true"/>
显示结果:
<input type="checkbox" name="checkboxField1" value="true" id="checkboxField1"/>
<input type="checkbox" name="checkboxField1" value="true" checked="checked" id="checkboxField1"/>
2. checkboxlist 标签
一次性创建多个复选框,它根据list属性指定的集合来生成多个复选框;
. listKey: 指定集合元素中的某个属性作为复选框的 value;
. listValue: 指定集合元素中的某个属性作为复选框的 标签;
3. combobox 标签
生成一个单行文本框和下拉列表框的组合,但两个表单元素只对应一个请求参数,只有单行文本框里的值才包含
请求参数,而下拉列表框则只是用于辅助输入,没有name属性。
4. doubleselect 标签
生成一个级联列表框, 当选择第一个下拉列表框时,第二个下拉列表框的内容会随之改变。
. list: 第一个下拉列表框的集合;
. listKey: 指定集合元素中某个属性作为第一个下拉列表框的value;
. listValue: 指定集合元素中某个属性作为第一个下拉列表框的标签;
. doubleList: 第二个下拉列表框的集合;
. doublelistKey: 指定集合元素中某个属性作为第二个下拉列表框的value;
. doublelistValue: 指定集合元素中某个属性作为第二个下拉列表框的标签;
. doublename: 第二个下拉列表框的 name 属性;
doubleselect标签一定得位于form中,且form须指定action属性.
四. 数据传输以及类型转换
1. 为什么要数据传输以及数据转换?
所有的MVC框架,都需要负责收集用户请求参数,并将请求参数传给应用的控制器组件。但所有的请求参数都是,
也只能是字符串数据类型,但Java是强数据类型语言,因此MVC框架必须将这些字符串请求参数转换成相应的数据
类型,这个工作是所有MVC框架均应提供的功能。
不仅于此,在请求处理结束,在格式化显示请求处理结果的时侯,我们也需将封装了结果数据的JavaBean属性值
转换为HTML页面上显示的内容。
数据的传输和转换贯穿于请求处理的整个生命周期。
Struts2通过内置的拦截器提供了非常强大的类型转换机制,也提供了很好的扩展性,开发者可以非常简单地开发出
自己的类型转换器,完成字符串和自定义复合类型的转换(例如将字符串到Student实例间转换). 如果类型转换中
出现异常,开发者也无须关心异常处理逻辑。Struts2的conversionError拦截器提供了非常强大的表现层数据处理
机制, 并且在页面上显示异常信息。总之, Struts2的类型转换器提供了非常强大的表现层处理机制,方便了开发者
的使用和操作。
2. OGNL和Struts2
Struts2的类型转换是基于OGNL表达式的,只要我们将HTML输入项命名为合法的OGNL表达式,就可以充分利用Struts2
的类型转换机制。
3. 内置的类型转换器
Struts2已经内建了字符串类型和如下类型间的类型转换器.
. Date: 日期格式使用用户请求所在Locale的SHORT格式;
. 数组:默认情况下,元素是字符串;
. 集合:在默认情况下,集合元素类型是String;
4. List中放置自定义的类型
1) 提交页面
<s:textfield name="users[0].name" label="User Name"/>
<s:textfield name="users[1].name" label="User Name"/>
<s:textfield name="users[2].name" label="User Name"/>
<s:textfield name="users[0].age" label="User Age"/>
<s:textfield name="users[1].age" label="User Age"/>
<s:textfield name="users[2].age" label="User Age"/>
2) Action
public class ListConversion {
private List<Student> users;
...;
}
其国com.briup.Student类为:
public class Student {
private String name;
private int age;
}
如果Action中List不使用范型,必须在Action同目录下新增一文件, 使名规则为:
Action类名-conversion.properties, 例如,可命名为: ListConversion-conversion.properties
内容为:
Element_list类型属性名称=list中所包含对象完整类名
例如,Element_users=com.briup.Student
3) 显示页面
User Name Number 3: <s:property value="users[2].name"/><br>
User Age Number 2: <s:property value="users[1].age"/><br>
5. Map中放置自定义的类型
1) 提交页面
<s:textfield name="students['one'].name" label="Name"/>
<s:textfield name="students['two'].name" label="Name"/>
<s:textfield name="students['three'].name" label="Name"/>
<s:textfield name="students['one'].age" label="Age"/>
<s:textfield name="students['two'].age" label="Age"/>
<s:textfield name="students['three'].age" label="Age"/>
2) Action
public class MapConversion {
private Map<Student> students;
...;
}
其国com.briup.Student类为:
public class Student {
private String name;
private int age;
}
如果Action中Map不使用范型,必须在Action同目录下新增一文件, 使名规则为:
Action类名-conversion.properties, 例如,可命名为: MapConversion-conversion.properties
内容为:
Element_map类型属性名称=map中value对象完整类名
Key_map类型属性名称=map中key对象完整类名
例如,Element_students=com.briup.Student
Key_students=java.lang.String
3) 显示页面
Age Number One: <s:property value="students['one'].age"/><br>
Name Number Two: <s:property value="students['two'].name"/><br>
6. 自定义类型转换器:
注:JSP页面控件名和Action中属性同名
1) 转换器实现类实现接口 org.apache.struts2.util.StrutsTypeConverter,实现二个方法:
//将请求字符转换成特定对象
public Object convertFromString(Map context, String[] values, Class toClass) {
System.out.println("----------From String-------------");
for(Object key: context.keySet()) {
System.out.printf("Key: %s/tValue: %s%n",
key,
context.get(key));
}
System.out.println("toClass: "+toClass);
String userString = values[0];
Circle newCircle = parseCircle(userString);
return newCircle;
}
//将对象转换成字符串输出
public String convertToString(Map context, Object o) {
System.out.println("----------From String-------------");
for(Object key: context.keySet()) {
System.out.printf("Key: %s/tValue: %s%n",
key,
context.get(key));
}
Circle circle = (Circle) o;
String userString = "toString: C:r" + circle.getRadius();
return userString;
}
2) 设置配置文件:
局部转换器: 在Action所在目录下配置文件:
Action类名-conversion.properties , 例如 CircleAction-conversion.properties,
内容为:
对象属性名称=转换器的完整类名 例如:circle=com.briup.CircleConverter
全局转换器: 在源文件根目录下配置文件:
xwork-conversion.properties,
内容为:
对象完整类名称=转换器的完整类名 例如:com.briup.Circle=com.briup.CircleConverter
五. 拦截器
1. 拦截器是什么?
多个Action中需要重复执行一些代码,我们可以将这些代码提取出来形成单独一个类中的方法,从而提供更好的重用
性。这个单独提供的类就是 所谓的拦截器。 也就是说 拦截器体现的其实是一种软件复用的原则。
拦截器就是一个类,也个类也包含方法,只是这个方法是个特殊方法,它会在目标方法调用之前或之后被调用执行。
拦截器体系是Struts2的一个重要组成部分, 正是大量的内建的拦截器完成了该框架的大部分操作。这些拦截器有的提供
公用功能,有的提供高级功能。例如通过 params 拦截器将 HTTP 请求中的参数解析出来,设置成 Action 的属性;
servlet-config拦截器直接将HTTP请求中的HttpServletRequest实例和HttpServletResponse实例传给Action;
fileUpload拦截器则负责解析请求参数中的文件域,并将一个文件域设置成Action的三个属性等;
2. 为什么需要拦截器?
1) 代码重用;
将重复代码从多个Action中剥离出来,实现代码重用,便于代码维护;例如一些日志,权限管理代码。
2) 使用移除方便;
Struts2中拦截器为了进一步的维护方便,将拦截器相关信息从源代码中移除,配置在配置文件中。这样开发者可以非常方便增
加或移除拦截器。
3. 拦截器是如何调用的?
1) FilterDispatcher实例化ActionProxy并调用其execute()方法;
2) 拦截器在请求抵达Action之前和响应抵达客户端之前被调用;
3) 一旦请求处理完毕,请求被送往Result渲染响应结果;
4. 拦截器
Struts2框架大多数核心功能由拦截器组成。例如重复提交、类型转换、数据填充、验证、文件上传、页面预处理以及更多功能
均得益于拦截器的帮助;
每一个拦截器均是通过在配置文件中配置完成;
5. 拦截器和Action
拦截器在调用的时侯,很可能要和Action交互。这时侯可以持有Action的引用,实现类似于解决重复提交或输入验证功能。拦截器
也可以在Action的execute()方法调用之前修改设置Action的状态;
6. 拦截器的配置
拦截器能基于每一个Action进行配置,自行定义的拦截器亦可和框架本身提供的拦截器混合使用;多个拦截器可以组合形成所
谓的拦截器栈。这时侯拦截器配置的位置对于执行的顺序非常重要。
7. 拦截器配置->具体代码
拦截器是通过配置文件指定,因此通过拦截器来引入通用操作的方式,完全是可插拨式的:当系统需要执行这些通用操作时,则
配置文件引用这些拦截器即可;如果系统不再需要执行这些通过操作,则在配置文件中取消引入这些拦截器即可。
在struts.xml文件中定义拦截器只需为拦截器指定一个拦截器名,就完成了拦截器定义。
<interceptor name="拦截器名" class="拦截器实现类"/>
一旦定义了拦截器和拦截器栈后,就可以使用这个拦截器来拦截Action了,拦截器的拦截行为将会在Action的execute()方法执行
之前被执行。
通过<interceptor-ref.../>可以在Action内使用拦截器。
1) 默认拦截器
每一个包只默置一个默认拦截器,一旦为某个包指定了默认拦截器,如果该包中Action没有显示指定拦截器,则默认的拦截器将会
起作用。但值得注意的是:一旦我们为该包中的Action显示指定了某个拦截器,则默认的拦截器不会起作。如果该Action需要
使用该默认拦截器,则必须手动配置该拦截器的引用。
配置默认拦截器使用<default-interceptor-ref.../>元素,该元素作为<package.../>元素的子元素使用,为该包下的所有
Action配置默认的拦截器。配置<default-interceptor-ref.../>元素时,需要指定一个name属性,该name属性值是一个已经
存在的拦截器的名字,表明将该拦截器配置成该包的默认拦截器。需要注意的是每个<package.../>元素只能有一个
<default-interceptor-ref.../>子元素,即每个包只能指定一个默认拦截器。
<package name="包名">
<interceptors>
<interceptor.../>
<interceptor-stack.../>
</interceptors>
<default-interceptor-ref name="拦截器名或拦截器栈名"/>
<action .../>
</package>
配置默认拦截器是一种使用拦截器的方式——避免在每个Action中单独配置拦截器,通过在该包下配置默认拦截器,可以实现
为该包下所有Action同时配置相同的拦截器。
当我们定义的包继承struts-default包时,也继承了它的默认拦截器栈:defaultStack,这就意味着,如果我们不为Action指定
任何的拦截器引用,则defaultStack拦截器栈将会拦截Action.
8. 拦截器栈
如果有多个Action在执行前同时做登录检查、安全检查和记录日志,则可以将这三个动作对应的拦截器组成一个拦截器栈。达到减少
配置,重复使用的目的。
配置拦截器栈的语法示例如下:
<interceptor-stack name="拦截器栈名">
<interceptor-ref name="拦截器一"/>
<interceptor-ref name="拦截器二"/>
...
</interceptor-stack>
当然拦载器栈中也可以再次包含拦载器:
<interceptor-stack name="拦截器栈一">
<interceptor-ref name="拦截器一"/>
<interceptor-ref name="拦截器二"/>
...
</interceptor-stack>
<interceptor-stack name="拦截器栈二">
<interceptor-ref name="拦截器一"/>
<interceptor-ref name="拦截器栈一"/>
...
</interceptor-stack>
9. 自定义拦截器
实现接口 com.opensymphony.xwork2.interceptor.Interceptor接口,该接口包含三个方法:
. init(): 拦截器实例化后立即执行,且在整个拦截器的生命周期中仅执行一次。主要用于打开一些一次性资源。
. destory(): 在拦截器实例被销毁之前调用,且在整个拦截器的生命周期中仅执行一次。主要用于关闭一些一次性资源。
. intercept(ActionInvocation invocation): 用户需要实现的拦截动作,就像Action的execute()方法一样,intercept()
方法会返回一个字符串作为逻辑视图。如果该方法直接返回了一个字符串,系统将会跳转至该逻辑视图对应的实
际视图资源,不会调用被拦截的Action。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以通
过调用该参数的invoke方法,将控制权转给下一个拦截器,或者转给Action的execute()方法;
10. 内置拦截器
1) 工具
a. timer: 负责输出Action的执行时间,在分析该Action的性能瓶颈时比较有用。
b. logger: 负责日志记录的拦截器, 主要是输出Action的名字;
2) 数据传输
a. params: 最基本的一个拦截器,它负责解析HTTP请求中的参数,并将参数值设置成 Action 对应的属性值;
b. static-params: 负责将xml中<action>标签下<param>标签名册中的参数传入action.
c. autowiring: 自动装配的拦截器,主要用于当 Struts2 和 Spring 整合时, Struts2 可以使用自动装配的方式
来访问 Spring 容器中的 Bean.
d. fileUpload: 主要用于文件上传,负责解析表单中文件域的内容;
e. servlet-config: 某个Action需要直接访问Servlet API, 就是通过这个拦截器实现的。
3) 工作流
a. workflow: 负责调用Action类中的validate方法,如果校验失败,则返回 input 的逻辑视图;
b. validation: 通过执行在 xxxAction-validation.xml中定义的校验器,从而完成数据校验;
c. prepare: 如果 action 实现了 Preparable 接口,将会调用该拦截器的 prepare() 方法;
d. model-driven: 这是一个用于模型驱动的拦截器,当某个 action 类实现了 ModelDriven 接口时,它负责把
getModel() 方法的结果堆入 ValueStack中;
4) 混合
a. exception: 负责处理异常, 它将结果映射为结果;
b. token: 阻止重复提交,它检查传到Action中的token, 从而防止多次提交;
c. token-session: 功能与token类同,只是将 token 保存在 HttpSession 中;
d. scoped-Model-Driven: 如果一个Action实现了一个ScopedModelDriven接口,该拦截器负责从指定生存范围中
找出指定的Model, 并将通过setModel()方法将该Model传给Action;
e. execAndWait: 后台执行action, 负责将等待画面发给用户;
六. 输入校验
1. 为什么需要输入校验?
对于一个Web应用而言,所有的用户数据都是通过浏览器收集的,用户的输入信息是非常复杂的:用户操作不熟练,输入出错,
硬件设备的不正常,网络传输的不稳定,甚至有恶意的蓄意破坏..., 这些都有可能导致输入异常;
输入的异常,轻则导致系统非正常中断,重则导致系统崩溃。应用程序必须能正常处理。对异常输入的过滤,就是输入校验,也
称为数据校验;通常的做法是碰到异常输入时应用程序直接返回,提示浏览者必须重新输入。
输入校验分为客户端校验和服务器校验,客户端校验主要是过滤正常用户的误操作,主要通过JavaScript代码完成;服务器端校
验是整个应用阻止非法数据的最后防线,主要通过在应用中编程实现。
Struts2提供了非常强大的输入校验体系,通过Struts2内建的输入校验器,Struts2应用无需书写任何输入校验代码,即可完成绝
大部分输入校验,并可以同时完成客户端校验和服务器端校验。当然, Struts2也允许客户自行提供校验器。
2. 服务器端校验
1) 手动校验
步骤:a. action继承ActionSupport, 重写方法
public void validate(){...}
如果出现错误,调用ActionSupport中所提供的addFieldError("提示内容key","提示内容")方法加入错误提示;
b. 在 struts.xml 文件中 action 中配置名称为 input 的 result, 用以显示错误信息;
c. 在名称为 input 的result对应的JSP页面,通过标记<s:fielderror/>显示错误信息;
<s:fielderror/> -> 显示所有错误信息;
<s:fielderror>
<s:param>消息提示名称</s:param>
</s:fielderror>
输入校验流程:
a. 类型转换器负责对字符串的请求参数执行类型转换,并将这些值设置成Action的属性值;
b. 在执行转换过程中可能出现异常,如果出现异常,将异常信息保存到ActionContext中,conversionError拦截器负责将其
封装到fieldError里,然后执行步骤3;如果转换过程中没有异常信息,则直接进入第3步;
c. 通过反射调用validateXxx()方法,其中Xxx是即将处理用户请求的处理逻辑所对应的方法名;
d. 调用Action类里的validate()方法;
e. 如果经过上面4步都没有出现fieldError,将调用Action里处理用户请求的处理方法,如果出现了fieldError, 系统将转入
input逻辑视图所指定的视图逻辑;
2) Xwork验证框架
正如你所见,当你不同的用例需要不同的验证规则时,手动验证使你的代码显得很混乱;依然要写很多代码,编程依然很烦琐,
代码复用不高。 Struts2提供了基于验证框架的输入校验,在这种校验方式下,所有的输入校验只需要通过指定简单的配置
文件即可。
采用框架验证与手动验证步骤基本相同,只不过第一步Action中无须重写validate()方法;
a. 构建 *-validation.xml 文件
采用Struts2的校验框架时,只需要为该Action指定一个校验文件即可。校验文件是一个XML配置文件,每一个Action都有一个
校验文件,该文件的文件名应用遵守如下规则:
<Action名字>-validation.xml
该文件应该被保存在与Action class 文件相同的路径下,便于管理。
增加了该校验文件后,其他部分无需任何修改,系统自动会加载该文件,当用户提交请求时,Struts2的校验框架会根据该
文件对用户请求进行校验。
b. 注册验证器
验证器也是一个Java类,一个实现了com.opensymphony.xwork.validator.Validator接口的类;
但验证器写好后必须注册,通过注册给该验证器指定一个逻辑名,以方便Struts2程序对该验证器的访问;
注册有二种方式:
i. 通过ValidatorFactory类编码注册:
通过调用类com.opensymphony.xwork.validator.ValidatorFactory类中静态方法
void registerValidator(String name, Class c);
实现。
ii. 通过校验器注册文件配置注册:
在classpath根路径下提供一个validators.xml文件,通过该文件进行配置;这个文件称之为校验器注册文件;
Struts2提供了大量的校验器,这些内建的校验器可以满足大部分应用的校验需求。这些内建的校验器也需
注册,它们采用了配置注册方式,校验器文件位于 xwork-2.0.1.jar 文件中 com/opensymphony/xwork2/validator/
validators/ 目录下,一个名为 default.xml 文件中。该文件就是 Struts2的默认校验器注册文件。
c. 内建校验器
. required: 必填校验器,要求指定字段必须有值;
. requiredstring: 必填字符器校验器,要求字段值必须非空且长度大于0;
i. trim: 是否在校验前截断被校验属性值前后的空白,该属性是可选的,默认是 true.
. stringlength: 字符串长度校验器, 要求校验字符长度必须在指定范围;
i. trim: 是否在校验前截断被校验属性值前后的空白,该属性是可选的,默认是 true.
ii. minLength: 指定字段值的最小长度,可选,不指定则最小长度不受限制;
iii.maxLength: 指定字段值的最大长度,可选,不指定则最大长度不受限制;
. int: 整数验校器,要求校验字段的整数值必须在指定范围内;
i. min: 指定该属性最小值,如不指定,则不检查最小值;
ii. max: 指定该属性最大值,如不指定,则不检查最大值;
. double: 浮点数验校器,要求校验字段的双精度浮点数值必须在指定范围内;
. date: 日期验校器,要求校验字段的日期值必须在指定范围内;
i. min: 指定该属性最小值,如不指定,则不检查最小值;示例:<param name="min">1990-01-01</param>
ii. max: 指定该属性最大值,如不指定,则不检查最大值;
. email: 邮件地址校验器,它要求被检查字段的字符如果非空,则必须是合法的邮件地址;它基于正则表达式进行校验;
. url: 网址校验器,它要求被检查字段字符如果非空,则必须是合法的URL地址;它基于正则表达式进行校验;
. conversion: 检查被校验字段在类型转换过程中是否出现错误。
i. fieldName: 指定校验的 Action 属性名,如采用字段校验器风格,则无须指定该参数;
ii. repopulateField: 类型转换失败,返回 input 页面时,类型转换失败的表单域是否保留原来的错误输入。
. expression: 表达式校验器,非字段校验器,它会按OGNL语法指定一个返回boolean类型值的表达式。当返回 true,
校验通过;该逻辑表达式基于 ValueStack 进行求值;
<validators>
<validator type="expression">
<param name="expression">...</param>
<message>Failed to meet Ognl Expression ...</message>
</validator>
</validators>
. visitor: 验证Action里的复合属性(自定义类型);
i. context: 指定校验规则文件的context
ii. appendPrefix: 指定校验失败后提示信息是否添加前缀
示例:
i. StudentAction-validation.xml
-----------------------------------------
<field name="student">
<field-validator type="visitor">
<param name="context">studentContext</param>
<param name="appendPrefix">true</param>
<message>STUDENT:</message>
</field-validator>
</field>
ii. Student-studentContext-validation.xml
-----------------------------------------
<field name="name">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>PLEASE input your name!</message>
</field-validator>
</field>
<field name="age">
<field-validator type="int">
<param name="min">1</param>
<param name="max">100</param>
<message>AGE must be between ${min} and ${max}!</message>
</field-validator>
</field>
iii. 错误页面显示:
-----------------------------------------
STUDENT: PLEASE input your name!
STUDENT: AGE must be between 1 and 100!
. fieldexpression: 功能类同于 expression, 适合于字段校验器,要求指定 fileName 要求校验的Action的属性名;
<validators>
<validator type="expression">
<param name="fileName">name</param>
<param name="expression">...</param>
<message>Failed to meet Ognl Expression ...</message>
</validator>
</validators>
. regex: 检查被校验字段是否匹配一个正则表达式;
i. fieldName: 指定校验的Action属性名;
ii. expression: 指定正则表达式
iii.caseSensitive: 指定正则表达式匹配时,是否区分大小写,默认值为 true;
3) 探索高级功能
a. 自定义验证器
步骤一:实现接口Validator或继承类FieldValidatorSupport
步骤二:在验证器中提供与参数同名的属性以及对应的setter/getter方法,重写valide()方法
public class MyValidator extends FieldValidatorSupport {
private String validName="";
public void validate(Object arg0) throws ValidationException {
String fieldName=this.getFieldName();
String fieldValue=(String)this.getFieldValue(fieldName, arg0);
if(validName.indexOf(fieldValue.trim().toLowerCase())!=-1) return;
addFieldError(fieldName,arg0);
}
public String getValidName() {
return validName;
}
public void setValidName(String validName) {
this.validName = validName.toLowerCase();
}
}
步骤三:在classpath中配置文件 validators.xml
<validators>
<validator name="validName" class="com.briup.MyValidator"/>
</validators>
步骤四:在验证文件中使用自定义验证器
<field-validator type="validName">
<param name="validName">zs,ls,ww</param>
<message>Invalid name, please try again!</message>
</field-validator>
b. 使用OGNL表达式语言无须写Java代码构建验证规则
4) 使用不同的context进行验证
一个Action可以处理多个请求,不同请求有不同的验证规则,可以使用不同的验证文件封装不同的验证规则;
1) struts.xml文件中:
<action name="studentLogin" class="com.briup.StudentAction" method="login">
<result name="success">/studentResult.jsp</result>
<result name="input">/studentLogin.jsp</result>
</action>
2) 验证文件应与Action同一个目录,名称应为 Action类名-action逻辑名-validation.xml
5) Short-circuiting验证
一个字段如果设置有多个验证器,多个验证器都会生效。从而显示多个错误提示。如果想在前面验证器验证
失败后后面验证器就不参与验证,可在前面验证器上显示设置属性short-circuit值为"true", 其默认值为
false. 示例:
<field name="name">
<field-validator type="stringlength" short-circuit="true">
<param name="minLength">1</param>
<param name="maxLength">10</param>
<message>Length must be between 1 and 10!</message>
</field-validator>
<field-validator type="validName">
<param name="validName">zs,ls,ww</param>
<message>Invalid name, please try again!</message>
</field-validator>
</field>
上述例子上name表单控件值如果长度不符,则validName类型的验证器不会被调用。
另 requiredstring 名称的验证器类型设置short-circuit="true"对后续验证器不起作用。
5) ExpressionValidator验证
使用OGNL表达式作为验证条件,示例:
<validators>
<validator type="expression">
<param name="expression">name.equals("ls")</param>
<message>INVALID NAME!</message>
</validator>
<field name="name">
<field-validator type="requiredstring">
<message>Name is required!</message>
</field-validator>
</validators>
上述例子中 name 为一属性名,使用expression验证器验证其值是否为 "ls". 其中"ls"为一个具体值直接写在
验证配置文件中,也可以来自静态常量或静态方法的返回值。示例:
<validators>
<validator type="expression">
<param name="expression">name.equals(@com.briup.Constant@NAME)</param>
<message>INVALID NAME!</message>
</validator>
</validators>
其中Constant类源文件为:
public class Constant {
public static final String NAME="ww";
public static String getName() {
return "sq";
}
}
注意:属性和方法的修饰符应设置为 public.
5) VisitorFieldValidator验证
多个Action类中都要验证相同JavaBean对象的各个属性,为了避免验证配置信息出现在多个配置文件中,可以
将JavaBean对象的验证信息单独写在一个文件中,然后各Action验证配置文件使用visitor类型验证器去引用。
示例:
a. 没有使用visitor类型验证器
i. JSP页面
login.jsp
---------------------------------------------------------------
<h3>Student Login</h3>
<s:form action="login">
<s:textfield name="name" label="name"/>
<s:textfield name="password" label="Password"/>
<s:submit value="Submit"/>
</s:form>
register.jsp
---------------------------------------------------------------
<h3>Student Register</h3>
<s:form action="register">
<s:textfield name="name" label="name"/>
<s:textfield name="password" label="Password"/>
<s:submit value="Submit"/>
</s:form>
ii. Action
StudentProAction.java
---------------------------------------------------------------
public class StudentProAction extends
ActionSupport implements ModelDriven<Student> {
private Student student=new Student();
public String login() {
return "success";
}
public String register() {
return "success";
}
public Student getModel() {
return student;
}
}
iii.Struts.xml
<action name="login" class="com.briup.StudentProAction" method="login">
<result>/loginResult.jsp</result>
<result name="input">/login.jsp</result>
</action>
<action name="register" class="com.briup.StudentProAction" method="register">
<result>/registerResult.jsp</result>
<result name="input">/register.jsp</result>
</action>
iii.验证配置文件:
StudentProAction-login-validation.xml(和StudentProAction同包)
---------------------------------------------------------------
<validators>
<field name="name"> <!-- 属性名应与表单控件同名,而不是Action中属性名-->
<field-validator type="requiredstring">
<message>Name is required!</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<message>Password is required!</message>
</field-validator>
</field>
</validators>
StudentProAction-register-validation.xml(和StudentProAction同包)
---------------------------------------------------------------
<validators>
<field name="name">
<field-validator type="requiredstring">
<message>Name is required!</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<message>Password is required!</message>
</field-validator>
</field>
</validators>
注意:验证配置文件中<field>中name属性值与表单控制同名,使用<s:textfield>即可显示错误提示信息,
否则,须通过<s:fielderror/>显示错误提示信息;
. <s:fielderror/>:显示所有field级别的错误提示信息;
. <s:fielderror>
<s:param>student.password</s:param>
</s:fielderror>
: 提定显示名称为student.password的field级别的错误提示信息;
b. 使用visitor类型验证器
其它地方与上同,只是配置文件和显示错误信息设置有点变动:
iii.验证配置文件:
StudentProAction-login-validation.xml(和StudentProAction同包)
---------------------------------------------------------------
<validators>
<field name="student"> <!-- 属性名与Action中属性同名-->
<field-validator type="requiredstring">
<message>Name is required!</message>
</field-validator>
</field>
</validators>
StudentProAction-register-validation.xml(和StudentProAction同包)
---------------------------------------------------------------
<validators>
<field name="student"> <!-- 属性名与Action中属性同名-->
<field-validator type="requiredstring">
<message>Name is required!</message>
</field-validator>
</field>
</validators>
Student-validation.xml(和Student类同包)
---------------------------------------------------------------
<validators>
<field name="name">
<field-validator type="requiredstring">
<message>Name is required!</message>
</field-validator>
</field>
<field name="password">
<field-validator type="requiredstring">
<message>Password is required!</message>
</field-validator>
</field>
</validators>
注意这时显示错误信息时须使用标记 <s:fielderror/>.
2. 客户端校验
在服务器端设置成功的基础上,在<s:form>标记中将属性validate设置为true即可。这时错误显示效果和服务器端
验证相同,只不过借助于javascript实现而已。
注意,借助于OGNL表达式使用expression类型的验证器还需使用服务器端验证实现。???
七. 国际化
1. JSP页面显示国际化信息:
<s:text name="login.title"/> <!-- login.title为资源文件key值 -->
<s:textfield name="name" key="login.name"/> <!-- login.name为资源文件key值 -->
<s:property value="%{getText('login.name')}"/> <!-- login.name为资源文件key值 -->
2. Action中获取国际化信息
getText("资源文件key值"); //getText为ActionSupport的方法
3. 配置文件中获取国际化信息
<message>${getText('login.name')</message> <!-- login.name为资源文件key值 -->