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 对应类型的值*/@Overridepublic 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 对应类型的值*/@Overridepublic 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";}@Overridepublic 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";
} }
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"/></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>
