8. 类型转换器
8.1 默认的类型转换器
<bean type="com.opensymphony.xwork2.conversion.impl.CollectionConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.CollectionConverter" scope="singleton"/>
<bean type="com.opensymphony.xwork2.conversion.impl.ArrayConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.ArrayConverter" scope="singleton"/>
<bean type="com.opensymphony.xwork2.conversion.impl.DateConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.DateConverter" scope="singleton"/>
<bean type="com.opensymphony.xwork2.conversion.impl.NumberConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.NumberConverter" scope="singleton"/>
<bean type="com.opensymphony.xwork2.conversion.impl.StringConverter" name="struts" class="com.opensymphony.xwork2.conversion.impl.StringConverter" scope="singleton"/>
- 常见的类型基本均可由String转换为相应类型
int和Integer
float和Float
long和Long
double和Double
char和Character
boolean和Boolean
Date:可以接收yyyy-MM-dd或yyyy-MM-dd HH:mm:ss格式字符串
数组:可以将多个同名参数,存放到数组
集合:可将数据保存到List、Map
8.2 自定义类型转换器
package typeconverters;
import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 自定义类型转化器
* 类型转化是双向的,页面到服务端的转换value是String[]
* @author mycomputer
*/
public class MyDateConverter extends DefaultTypeConverter {
/**
* 值类型转换
* @param value 要转换的值
* @param toType 要转换的类型
* @return 对应类型的值
*/
@Override
public Object convertValue(Object value, Class toType) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
try {
//由客户端到服务端
if (toType == Date.class) {
return sdf.parse(((String[]) value)[0]);
}
//由服务端到客户端
else if (toType == String.class){
return sdf.format((Date)value);
}
} catch (ParseException e) {
e.printStackTrace();
}
return super.convertValue(value,toType);
}
}
8.3 局部类型转换的注册
- 仅针对于指定Action起作用,注册方式:在Action类所在包下,创建一个ActionClassName-conversion.properties文件(ActionClassName是具体Action类的类型-conversion.properties是固定写法),该文件的内容格式为:属性名称=类型转换器的全类名
自定义类型转换器的配置有两中方式: 在使用局部自定义类型转换器时,在对应Action类所在的包下新建xxxAction-conversion.properties文件,其他配置一切正常,页面提交数据至Action时未经过注册转换器,注册失败,原因如下:
- 基于字段的配置:
在字段所在的Model(可能是Action,可能是一个JavaBean)的包下,新建一个ModelClassName-conversion.properties文件。 在该文件中输入键值对:fieldName=类型转换器的去类名。 在新建xxx-conversion.properties时参照的位置并非是xxxAction类,而是要转换字段所在的类,注册文件名应为ModelClassName-conversion.properties 2.基于类型的配置: 在src下新建xwork-conversion.properties文件; 键入:待转换的类型=类型转换器的全类名。
- 数据格式错误返回错误信息
通过让Action继承ActionSupport
通过在struts.xml对应action的result里面添加以下内容使其回到原页面
<result name="input">/index.jsp</result>
- 数据回显(通过struts标签实现form表达数据的提交)
<!-- 缺点:错误信息对用户不友好 -->
<s:form action="zhuce" method="POST">
<s:textfield name="birthday" label="生日"/>
<s:submit value="登录"/>
</s:form>
- 类型转换异常信息修改
在Action所在包中添加一个ActionClassName.properties的属性文件,在该文件中写入内容,invalid.fieldvalue.变量名=异常提示信息
- 实现多种时间格式验证
package typeconverters;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.conversion.TypeConversionException;
import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Pattern;
/**
* 自定义类型转化器
* 类型转化是双向的,页面到服务端的转换value是String[]
* @author mycomputer
*/
public class MyDateConverter extends DefaultTypeConverter {
/**
* 值类型转换
* @param value 要转换的值
* @param toType 要转换的类型
* @return 对应类型的值
*/
@Override
public Object convertValue(Object value, Class toType) {
SimpleDateFormat sdf = null;
try {
//由客户端到服务端
if (toType == Date.class) {
//实现多种时间格式验证
sdf = getSimpleDateFormat(((String[]) value)[0]);
//存到域空间中
ActionContext.getContext().getSession().put("sdf",sdf);
return sdf.parse(((String[]) value)[0]);
}
//由服务端到客户端
else if (toType == String.class){
sdf = (SimpleDateFormat) ActionContext.getContext().getSession().get("sdf");
return sdf.format((Date)value);
}
} catch (ParseException e) {
e.printStackTrace();
}
return super.convertValue(value,toType);
}
private SimpleDateFormat getSimpleDateFormat(String source) {
SimpleDateFormat sdf = null;
if (Pattern.matches("^\\d{4}-\\d{2}-\\d{2}$",source)){
sdf = new SimpleDateFormat("yyyy-MM-dd");
}else if (Pattern.matches("^\\d{4}/\\d{2}/\\d{2}$",source)){
sdf = new SimpleDateFormat("yyyy/MM/dd");
}else if (Pattern.matches("^\\d{4}\\d{2}\\d{2}$",source)){
sdf = new SimpleDateFormat("yyyyMMdd");
}else {
throw new TypeConversionException();
}
return sdf;
}
}
9. 数据验证
- 为防止客户端传来的数据引发程序异常,除了需要进行客户端验证,还需要进行服务端验证
9.1 手工编写代码实现对Action所有方法进行验证
package actions;
import com.opensymphony.xwork2.ActionSupport;
import java.util.Date;
import java.util.regex.Pattern;
/**
* 继承ActionSupport类,重写validate方法
* @author mycomputer
*/
public class LoginAction extends ActionSupport{
private Date birthday;
private int age;
private String name;
private String mobile;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String zhuce(){
return "success";
}
public String dosome(){
return "success";
}
@Override
public void validate() {
if (name == null || "".equals(name.trim())){
//如果fieldErrors.size() > 0,则自动跳转到input视图
this.addFieldError("name","用户名不能为空");
}
if (mobile == null || "".equals(mobile.trim())){
this.addFieldError("mobile","电话不能为空");
}else if (Pattern.matches("^1[345789]\\d{9}$",mobile)){
this.addFieldError("mobile","电话格式有错");
}
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>注册页面</title>
</head>
<body>
<s:fielderror/>
<s:form action="zhuce" method="POST">
<s:textfield name="age" label="年龄"/>
<s:textfield name="birthday" label="生日"/>
<s:textfield name="name" label="名字"/>
<s:textfield name="mobile" label="电话"/>
<s:submit value="登录"/>
</s:form>
</body>
</html>
9.2 手工编写代码实现对Action指定方法进行验证
//在实现对Action所有方法进行验证基础上,将validate方法名之后加上指定方法的方法名,
//方法名首字母大写
public void validateDosome() {
if (name == null || "".equals(name.trim())){
this.addFieldError("name","用户名不能为空");
}
if (mobile == null || "".equals(mobile.trim())){
this.addFieldError("mobile","电话不能为空");
}else if (Pattern.matches("^1[345789]\\d{9}$",mobile)){
this.addFieldError("mobile","电话格式有错");
}
}
9.3 基于XML配置方式实现输入数据验证
- 使用XML配置方式实现输入数据的验证,Action类仍需要继承自ActionSupport类。验证仍分为两类:对Action中所有方法执行前的验证、对Action 中指定方法执行前的验证
- 该验证方式需要使用XML配置文件,该配置文件的约束,即文件头部,在xwork-core-2.3.24.jar中的根下的xwork-validatior-1.0.3.dtd中可以找到
- 对Action中所有方法执行前的验证
- 在 Action类所在的包中放入一个XML配置文件,该文件的取名应遵守ActionClassName-validation.xml规则
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE validators PUBLIC
"-//Apache Struts//XWork Validator 1.0.3//EN"
"http://struts.apache.org/dtds/xwork-validator-1.0.3.dtd">
<validators>
<field name="name">
<field-validator type="requiredstring">
<!--<param name="trim">true</param>-->
<message>用户名不能为空</message>
</field-validator>
</field>
<field name="mobile">
<field-validator type="requiredstring">
<message>电话不能为空</message>
</field-validator>
<field-validator type="regex">
<param name="regex"><![CDATA[
^1[345789]\d{9}$
]]></param>
<message>电话格式不正确</message>
</field-validator>
</field>
</validators>
- 对Action中指定方法执行前的验证
- 更改XML文件的文件名命名规则为:ActionClassName-ActionName_MethodName-validation.xml
9.4 常用验证器的用法
- required:非空(必填)验证
<field-validator type="required">
<message>性别不能为空</message>
</field-validator>
- requiredstring:非空字符串验证
<field-validator type="requiredstring">
<!--<param name="trim">true</param>-->
<message>用户名不能为空</message>
</field-validator>
- fieldexpression:字段表达式验证
<field-validator type="fieldexpression">
<!-- 表达式只能是== -->
<param name="expression">pwd == repwd</param>
<message>确认密码与密码不一致</message>
</field-validator>
- stringlength:字符串长度验证
<field-validator type="stringlength">
<param name="minLength">2</param>
<param name="maxLength">10</param>
<param name="trim">true</param>
<message>产品名称应在${minLength}-${maxLength}之间</message>
</field-validator>
- int:整数范围校验
<field-validator type="int">
<param name="min">1</param>
<param name="max">150</param>
<message>年龄须在1-150之间</message>
</field-validator>
注意,int、long、short、double 与 date校验器均继承自RangeValidatorSupport
类,是范围校验器,即不对数据类型进行校验,只对其有效范围进行校验。它们均有两个参数min与max。
- email:邮件验证
<field-validator type="email">
<message>电子邮件无效</message>
</field-validator>
- regex:正则表达式验证
<!-- <![CDATA[]]>成为cDate区,用于存放特殊表达式 -->
<field-validator type="regex">
<param name="regex"><![CDATA[
^1[345789]\d{9}$
]]></param>
<message>电话格式不正确</message>
</field-validator>
9.5 Action方法的执行流程
1、类型转换
类型转换失败是在Action调用相应属性的set方法之前发生,类型转换失败,不影响程序的运行
2、set方法
无论类型转换是否成功,都将执行该属性的set方法。当类型转换失败,会设置该属性值为null
3、数据验证
当验证器对通过set方法设置的属性值进行验证时,若验证失败,则会将验证失败信息写入到一个专门的集含中 fieldErrors集合。当所有数据验证完毕后,若fieldErrors集合中存在异常信息,即集合的size()大于0,则workFlow拦截器会返回一个”input”字符串,使请求转发到input视图。请求将无法到达 Action,即无法执行Action方法;当然,若在类型转换时已经发生异常,不仅会将异常信息写入到fieldErrors集合,还会继承执该属性的set方法,将null值赋予该属性。该属性接收到的为null值,肯定不是用户输入的期望的值,所以在进行数据验证时,也一定会验证失败。此时的验证失败信息也同样会写入到 fieldErrors集合
4、Action方法
经过上面的执行,如果系统中的fieldErrors集合存在异常信息,系统会自动将请求转发至input视图。如果系统中的fieldErrors没有任何异常信息,系统将执行Action方法
10. 拦截器
拦截器是 Struts2的一个重要特性。因为Struts2的大多数核心功能都是通过拦截器实现的。拦截器之所以称之为”拦截器“,是因为它可以在执行Action方法之前拦截住用户请求,执行一些操作;或在 Action方法执行之后拦截住向用户发送的响应,执行一些操作。即在Acton方法执行之前或之后执行,以增强 Action方法的功能。Struts2内置了很多拦截器,每个拦截器完成相对独立的功能,多个拦截器的组合体称为拦截器栈。最为重要的拦截器栈是系统默认的拦截器栈DefaultStack。拦截器定义在struts2-core-2.3.24.jar!struts-default.xml 中
实现拦截器: ```java package interceptors;
import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
/**
- 也可实现Interceptor接口
@author mycomputer */ public class MyInterceptor extends AbstractInterceptor {
@Override public String intercept(ActionInvocation actionInvocation) throws Exception {
Object user = ActionContext.getContext().getSession().get("user");
if (user != null){
//调用Action方法
return actionInvocation.invoke();
}
return "fail";
} }
<package name="zhuce" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="myinster" class="interceptors.MyInterceptor"/>
</interceptors>
<action name="zhuce" class="actions.LoginAction" method="zhuce">
<result name="success">/success.jsp</result>
<result name="input">/index.jsp</result>
<result name="fail">/index.jsp</result>
<interceptor-ref name="myinster"/>
<interceptor-ref name="defaultStack"/>
</action>
</package>
- 定义、注册拦截器(通过拦截器栈)
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="zhuce" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="myinster" class="interceptors.MyInterceptor"/>
<interceptor-stack name="mystack">
<interceptor-ref name="myinster"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<action name="zhuce" class="actions.LoginAction" method="zhuce">
<result name="success">/success.jsp</result>
<result name="input">/index.jsp</result>
<result name="fail">/index.jsp</result>
<interceptor-ref name="mystack"/>
</action>
</package>
</struts>
- 默认拦截器的注册 ```xml <?xml version=”1.0” encoding=”UTF-8” ?> <!DOCTYPE struts PUBLIC “-//Apache Software Foundation//DTD Struts Configuration 2.3//EN” “http://struts.apache.org/dtds/struts-2.3.dtd">
<package name="zhuce" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="myinster" class="interceptors.MyInterceptor"/>
<interceptor-stack name="mystack">
<interceptor-ref name="myinster"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!-- 注册到默认拦截器,需注意action里面不要写
<interceptor-ref name="mystack"/>覆盖了默认拦截器 -->
<default-interceptor-ref name="mystack"/>
<action name="zhuce" class="actions.LoginAction" method="zhuce">
<result name="success">/success.jsp</result>
<result name="input">/index.jsp</result>
<result name="fail">/index.jsp</result>
</action>
</package>
<a name="uEiiG"></a>
#### 10.1 方法过滤拦截器
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="zhuce" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="myinster" class="interceptors.MyInterceptor">
<!-- 拦截doFirst,doSecond方法 -->
<!--<param name="includeMethods">doFirst,doSecond</param>-->
<!-- 除了doThird方法都拦截 -->
<param name="excludeMethods">doThird</param>
</interceptor>
<interceptor-stack name="mystack">
<interceptor-ref name="myinster"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="mystack"/>
<action name="zhuce" class="actions.LoginAction" method="zhuce">
<result name="success">/success.jsp</result>
<result name="input">/index.jsp</result>
<result name="fail">/index.jsp</result>
</action>
</package>
</struts>
10.2 拦截器的执行顺序
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="zhuce" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="oneInterceptor" class="interceptors.MyInterceptor">
<param name="excludeMethods">doThird</param>
</interceptor>
<interceptor name="twoInterceptor" class="interceptors.MyInterceptorTwo">
<param name="includeMethods">doFirst,doSecond</param>
</interceptor>
<interceptor-stack name="mystack">
<!-- 拦截器的执行顺序与注册的顺序是一致的 -->
<interceptor-ref name="oneInterceptor"/>
<interceptor-ref name="twoInterceptor"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="mystack"/>
<action name="zhuce" class="actions.LoginAction" method="zhuce">
<result name="success">/success.jsp</result>
<result name="input">/index.jsp</result>
<result name="fail">/index.jsp</result>
</action>
</package>
</struts>