MVC框架具有将字符串请求参数转换为响应数据类型的功能,而Struts2.0作为MVC框架的实现者,理所当然提供了类型转换机制,概述如下:
- HTTP没有“类型”的概念,表单输入的只可能是字符串或字符串数组;
- Struts2从表单到Action对象的类型转换即是从字符串到非字符串的;
- Struts2的params拦截器在将请求参数映射到Action对象属性时,可自动完成字符串和基本数据类型间的转换。
一、类型转换错误消息的显示与定制
1.1 类型转换错误
ConversionError拦截器作为默认拦截器栈的一员,负责添加与类型转换有关的出错消息(前提是Action类实现了ValidationAware接口)和保存各请求参数的原始值。
若字段标签使用非simple主题,则类型转换错误消息为:”Invalid field value for field fieldName”。
如果类型转换发生错误,则
- 若Action类未实现ValidationAware接口:Struts2发生类型转换错误时,仍会继续调用其Action方法,就好像什么都没发生一样;
- 若Action类实现了ValidationAware接口:Struts2发生类型转换错误时,不会继续调用其Action方法;而将检查相关action元素的声明是否包含name=input的result元素,若有则将控制权转交给该元素,否则抛出异常。
- 注意:可以直接继承于实现了ValidationAware接口的ActionSupport类来代替实现ValidationAware接口。
1.2 覆盖默认错误信息
- 新建属性文件ActionClassName.properties,放在Action类所在包中;
- 添加键值对:invalid.fieldvalue.fieldName=Custome error message。
1.3 强制显示错误信息
如果表单是simple主题,则不会自动显示错误消息。解决方案如下:
- 使用EL或OGNL表达式显示:
${fieldErrors.age[0]}
,即通过debug标签可知,类型转换出错时在值栈的Action对象中有一个类型为Map<String, List<String>>
的fieldErrors属性; - 使用s:fielderror标签来显示:
<s:fielderror fieldName="fieldname"></s:fielderror>
,即使用其fieldName属性显示指定字段的错误信息。
问题:若是simple主题,且用s:fielderror标签来显示错误消息,则该消息包含在一个ul、li、span中,影响页面排版,如何去除?
- 在src下新建template.simple包,并新建fielderror.ftl文件;
- 将Struts2核心包/template/simple/fielderror.ftl文件内容复制到新建fielderror.ftl文件中;
- 剔除掉影响页面排版的ul、li、span部分即可。
二、自定义类型转换器
因为Struts2不能自动完成字符串到非基本数据类型的转换,故需要定制类型转换器,主要包括开发类型转换器类和配置类型转换器两部分;具体如下:
2.1 开发类型转换器类
自定义类型转换器必须实现ongl.TypeConverter接口或该接口某种具体实现类进行扩展,常扩展StrutsTypeConverter类即可。
2.2 配置类型转换器
(1)基于字段的配置
该方式可以为某Model类(可能是Action也可能是JavaBean) 的各个属性分别配置一个自定义类型转换器;配置方法如下:
- 新建属性文件ModelClassName-conversion.properties,放在Model类所在包下;
- 添加键值对:字段名=类型转换器的全类名;
- 注意:第一次使用该转换器时即创建实例(单实例的)……
(2)基于类型的配置
该方式为整个应用中所有Model类的各个属性均配置同一个自定义类型转换器,配置方法如下:
- 在src目录下新建xwork-conversion.properties文件;
- 添加键值对:待转换的类型=类型转换器的全类名;
- 注意:在当前Struts2应用被加载时创建实例(创建两次)……
2.3 实现自定义的时间类型转换器
要求:时间pattern需要以web应用初始化参数的形式配置在web.xml中,且当类型转换失败给出自定义信息。
web.xml中配置初始化参数如下:
<context-param>
<param-name>pattern</param-name>
<param-value>yyyy-mm-dd hh:mm:ss</param-value>
</context-param>
类型转换器DateConverter.java代码如下:
package com.qiaobc.struts.converter;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import javax.servlet.ServletContext;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.util.StrutsTypeConverter;
public class DateConverter extends StrutsTypeConverter {
private DateFormat dateFormat;
public DateConverter() {
System.out.println("DateConverter...");
// 获取应用程序的初始化参数
/**
* 基于字段的配置采用如下方法获取初始化参数,因其在第一次使用类型转换器时创建实例
*/
// ServletContext servletContext = ServletActionContext.getServletContext();
// String dateStr = servletContext.getInitParameter("pattern");
// dateFormat = new SimpleDateFormat(dateStr);
}
/**
* 基于类型的配置采用如下方法获取初始化参数,因其在应用加载时即创建实例
*/
public DateFormat getdDateFormat() {
if(dateFormat == null) {
ServletContext servletContext = ServletActionContext.getServletContext();
String dateStr = servletContext.getInitParameter("pattern");
dateFormat = new SimpleDateFormat(dateStr);
}
return dateFormat;
}
@Override
public Object convertFromString(Map context, String[] values, Class toClass) {
System.out.println("convertFromString...");
if(toClass == Date.class) {
if(values != null && values.length > 0) {
// 将values[0]转换为Date型
try {
// 基于字段的配置采用如下方法
// return dateFormat.parse(values[0]);
// 基于类型的配置采用如下方法
return getdDateFormat().parse(values[0]);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
// 未转换成功则返回values
return values;
}
@Override
public String convertToString(Map context, Object o) {
System.out.println("convertToString...");
if(o instanceof Date) {
Date date = (Date) o;
return dateFormat.format(date);
}
// 未转换成功则返回null
return null;
}
}
三、类型转换器与复杂属性&集合的协同使用
类型转换器与复杂属性协同使用的基本原理图如下所示:
类型转换器与集合协同使用的基本原理图如下所示: