1、struts2类型转换
需求:(注册)
<body> <form action="${pageContext.request.contextPath}/converter/userAction_save.do" name="form1" method="post"> 编号:<input type="text" name="id"><br/> 姓名:<input type="text" name="userName"><br/> 出生日期:<input type="text" name="birth"><br/> <input type="submit" value="提交"><br/> </form> </body>
根据需求写action以及一些配置就不多说了。
1、获取表单元素的值
思考:
怎么将表单中的数据在action的对应方法中得到?
方式一:
我们在jsp+servlet的时候就知道可以使用request.getParameter获得,所以:
public String save() throws Exception { System.out.println("UserAction ****** save"); /* web方式获取: HttpServletRequest request=ServletActionContext.getRequest(); String username=request.getParameter("userName"); System.out.println("username ="+username ); */
方式二:
Struts2其实有机制实现了表单数据的获取,原生的使用就有点low了。
/* *在struts2框架中,在对应动作类action中 *声明与页面中表单元素同名的属性,给出对应的set和get方法 *struts2框架就会根据反射机制,获取页面中表单元素的值 */ private Integer id; private String userName; private Date birth;
//省略get,set方法 public String save() throws Exception { System.out.println("UserAction ****** save"); /* web方式获取: HttpServletRequest request=ServletActionContext.getRequest(); String username=request.getParameter("userName"); System.out.println("username ="+username ); */ System.out.println("id = "+id); System.out.println("userName = "+userName); System.out.println("birth = "+birth); return "success"; }
前面我们获取的表单元素每一个元素都是在userAction中对应一个属性,如果我们希望有一些属性是在userAction类中的对象属性的某一个字段的内容,它自动注入,应该怎么处理呢?
我就不解释了,一看看笔记看代码。这里可以做到bean的属性注入,和Collection集合bean的指定属性注入。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'userform.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <form action="${pageContext.request.contextPath}/converter/userAction_save.do" name="form1" method="post"> 编号:<input type="text" name="id"><br/> 姓名:<input type="text" name="userName"><br/> 出生日期:<input type="text" name="birth"><br/> 学历编号:<input type="text" name="edu.id"><br/> 学历名称:<input type="text" name="edu.name"><br/> 员工姓名:<input type="text" name="emps[0].name"><br/> 员工薪水:<input type="text" name="emps[0].salary"><br/> 员工姓名:<input type="text" name="emps[1].name"><br/> 员工薪水:<input type="text" name="emps[1].salary"><br/> <input type="submit" value="提交"><br/> </form> </body> </html>
package cn.itcast.converter; import java.util.Collection; import java.util.Date; import com.opensymphony.xwork2.ActionSupport; public class UserAction extends ActionSupport { private static final long serialVersionUID = 1L; /* *在struts2框架中,在对应动作类action中 *声明与页面中表单元素同名的属性,给出对应的set和get方法 *struts2框架就会根据反射机制,获取页面中表单元素的值 */ private Integer id; private String userName; private Date birth; private Edu edu; private Collection<Employee> emps; public String save() throws Exception { System.out.println("UserAction ****** save"); /* web方式获取: HttpServletRequest request=ServletActionContext.getRequest(); String username=request.getParameter("userName"); System.out.println("username ="+username ); */ System.out.println("id = "+id); System.out.println("userName = "+userName); System.out.println("birth = "+birth); System.out.println("edu.id = "+edu.getId()); System.out.println("edu.name = "+edu.getName()); for(Employee emp:emps){ System.out.println(emp.getName()+"--"+emp.getSalary()); } return "success"; } public void setBirth(Date birth) { this.birth = birth; } public Date getBirth() { return birth; } public void setEdu(Edu edu) { this.edu = edu; } public Edu getEdu() { return edu; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override public String execute() throws Exception { System.out.println("UserAction ****** execute"); return "success"; } public void setEmps(Collection<Employee> emps) { this.emps = emps; } public Collection<Employee> getEmps() { return emps; } }
2、struts2的类型自动转换
我们前面发现struts2的这种机制可以使用反射将参数值自动注入。但是我们知道request.getParameter()得到的值都是String类型的,而我们定义的类型是各种各样的。那么struts2是否可以自动帮我们完成所有的转换呢?
测试得到:
对于基本数据类型和String类型来说,struts2可以完美的完成转换类型的功能。
对于Date类型来说,必须限制date类型输入的是yyyy-MM-dd 格式才可以成功转换,否则会报错。
Unexpected Exception caught setting 'birth' on 'class cn.itcast.converter.UserAction: Error setting expression 'birth' with value ['2012/01/01', ]
所以我们知道,struts2不是万能的,只能对那几个简单的数据类型进行数据转换,其他的就需要我们自动设置转换器了。
注意:
出现类型转换错误的途中可能也冒出这样一个错误:
No result defined for action cn.itcast.converter.UserAction and result input
解决方案:
在action的result中配置错误结果处理页面。
<package name="converter" namespace="/converter" extends="struts-default"> <action name="userAction_save" class="cn.itcast.converter.UserAction" method="save"> <!-- 错误提示:No result defined for action cn.itcast.converter.UserAction and result input 配置如果出错的时候,自动化转向到错误页面 --> <result name="success">/converter/success.jsp</result> <result name="input">/converter/error.jsp</result> </action> </package>
3、自定义类型转换器
定制类型转换器说明:
自定义类型转换器必须实现TypeConverter接口或对这个接口的某种具体实现做扩展
1、类型转换器的书写:
例子:
package cn.itcast.converter; import java.text.ParseException; import java.text.SimpleDateFormat; import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter; /* * 自定义转换器 * 作用:把页面中birth元素的字符串内容内容转换成java.util.Date */ public class DateConverter extends DefaultTypeConverter { @Override public Object convertValue(Object value, Class toType) { //要转换的值 [Ljava.lang.String;@27d314cc // System.out.println("value = "+value); //要转换的类型: class java.util.Date //System.out.println("toType = "+toType); if(value==null){ return false; } if(toType==null){ return false; } if(toType!=java.util.Date.class){ return false; } if(value instanceof java.lang.String[]){ String[] strs=(String[]) value; if(strs[0]!=null && !strs[0].trim().isEmpty()){ try { //先确定日期的格式 SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd"); return sdf.parse(strs[0]); } catch (ParseException e) { /* * 在struts2框架里,自定义的类型转换器 * 如果我们不手动抛出异常,struts2框架只捕获异常,但是并不抛出 * 所以框架就会认为类型转换器转换成功,转向成功页面 */ throw new RuntimeException(e); } } } return false; } }
注意几个点:
Value是一个字符串数组而不是字符串
Return false;表示转换失败!
如果你仅仅是在自定义类型转换器中try catch处理了异常,struts2框架会认为你这里没有问题,也就是转换成功,所以try/catch之后一定要抛出去,这样框架才会知道使用这个自定义转换器也转换失败,转向失败页面
2、配置自定义的类型转换器
在应用程序里使用一个自定义的类型转换器之前,必须先对它进行配置。
这种配置既可以基于字段,也可以基于类
1、基于字段配置(局部):
可以为某个动作的各个属性分别指定一个自定义的转换器
1、 创建一个属性文件:ActionClassName-conversion.properties,该文件需和相对应的动作类(Action)放在同一个目录下
ActionClassName是Action的类名,后面的-conversion.properties是固定写法
在properties文件中的内容为:
属性名称=类型转换器的全类名
对于本例子而言,文件的名称应为UserAction-conversion.properties
2、 编辑属性文件
Birth=cn.itcast.converter.DateConverter
2、基于类配置(全局):
1、在WEB-INF/classes目录(也就是src)下创建xwork-conversion.properties文件
在properties文件中的内容为:
待转换的类型=类型转换器的全类名
对于本例子而言,xwork-conversion.properties文件中的内容为:
Java.util.Date=cn.itcast.conberter.DateConverter
4、类型转换中的异常处理
1、打印属性错误异常信息
前面知道,在自定义类型转换器中的异常需要抛出去。这样框架才会跳转到错误页面。
我们前面的错误页面:
<body>
失败! !!<br>
</body>
仅仅告诉我们一个失败,这样是不够的。我们希望还能知道是哪一个属性转换失败,可以更改成为这样:
<body> 失败! !!<br> <s:fielderror fieldName="birth"/> </body>
在error.jsp页面中使用<s:fielderror fieldName=””/>打印所有转化的异常的错误信息
filedName:指定Action属性的名称
打印某个属性转化的异常错误信息
2、将显示的异常错误信息变成中文
为什么显示出来的错误信息是英文的呢?
错误信息是一个资源文件,默认的在xwork-core-2.3.jar下的com/opensymphony/xwork2的xwork-messages.properties
那么如何让显示出来的错误信息是中文的呢?
定义一个资源文件,覆盖之前定义的错误信息就好了
做法:
在与当前Action同级目录中,新建converter.properties(文件名称自定义)资源文件
在该文件中可以增加如下内容:
Xwork-default.invalid.fieldvalue=无效的字段值”{0}”
资源文件创建好了,如何注入资源文件到struts2框架中呢?
<!-- 加载国际化资源文件,如果有多个资源文件用逗号隔开,后缀名.properties省略 其他的资源文件也是这样加载 --> <constant name="struts.custom.i18n.resources" value="cn.itcast.converter.converter"/>
3、将异常提示信息确定到某个字段上
我们可以发现,每一个字段出现转换异常给出的错误提示都是前面设置的无效的字段值,感觉不是很爽,我们希望将针对性变小来。
针对每个字段给出提示信息:
在前面的converter.properties中添加如下内容
格式: invalid.fieldvalue.xx=提示信息
内容: invalid.fieldvalue.birth=出生日期转换异常
一些代码与帮助文档:
<?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"> <!-- 书写配置文件找dtd,打开webApp,找到struts2.core下的2.3.dtd,直接复制这部分即可 --> <struts> <!-- constant:配置常量 name:指定的是struts2框架底层提供的default.properties资源文件中配置的“常量” value:指定的是配置常量的值 在struts.xml中配置的常量的值会覆盖底层提供的default.priperties资源文件中配置的常量的值 配置struts2框架中的action的后缀名,如果有多个,使用“,”隔开 如果资源文件在struts.properties和struts.xml中都配置了,那么struts.properties中的生效 因为常量可以在多个配置文件中定义,所以我们需要了解下struts2加载常量的搜索顺序: 1 struts-default.xml 2 struts-plugin.xml 3 struts.xml 4 struts.properties 5 web.xml --> <!-- action后缀名 --> <constant name="struts.action.extension" value="do,love"/> <!-- 配置国际化资源文件修改时,是否重新加载。默认是false,为不加载 --> <!-- <constant name="struts.il8n.reload" value="true"/> --> <!-- 配置struts2框架的配置文件修改时,是否重新加载。默认是false为不加载 --> <!-- <constant name="struts.configuration.xml.reload" value="true"/> --> <!-- 配置struts2框架的模式 默认为false,是生产模式 true是开发模式,需要更多的调试信息 ### when set to true, resource bundles will be reloaded on _every_ request. ### this is good during development, but should never be used in production ### struts.i18n.reload=false --> <constant name="struts.devMode" value="true" /> <!--关闭 动态方法调用 --> <constant name="struts.enable.DynamicMethodInvocation" value="false" /> <!-- 加载国际化资源文件,如果有多个资源文件用逗号隔开,后缀名.properties省略 其他的资源文件也是这样加载 --> <constant name="struts.custom.i18n.resources" value="cn.itcast.converter.converter"/> <!-- package:包 name:包名,唯一的,必选项 namespace:命名空间,唯一的,相当于房间号。可选项,省略情况下是“/” extends:继承 extends=“struts-default”:struts2框架底层提供的核心包struts2-core-2.3.3.jar下的struts-default.xml文件 为什么要继承这个struts-default.xml文件 因为struts框架底层提供的struts-default.xml声明了所有的拦截器和拦截器栈。 我们知道struts2框架运行时执行struts-default.xml中的拦截器栈完成必要功能。 如果不继承struts-default.xml文件,就没有办法使用struts2提供的所有拦截器。 --> <package name="default" namespace="/" extends="struts-default"> <default-action-ref name="notFoundAction"/> <action name="notFoundAction" class="cn.itcast.util.action.NotFoundAction"> <result>/404.jsp</result> </action> </package> <!-- 引入自定义配置文件 --> <include file="cn/itcast/helloworld/struts_helloworld.xml"/> <include file="cn/itcast/prima/struts_prima.xml"/> <include file="cn/itcast/resulttype/struts_resulttype.xml"/> <include file="cn/itcast/pattern/struts_pattern.xml"/> <include file="cn/itcast/converter/struts_converter.xml"/> </struts>
<?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> <!-- action="${pageContext.request.contextPath}/converter/userAction_save.do" --> <package name="converter" namespace="/converter" extends="struts-default"> <action name="userAction_save" class="cn.itcast.converter.UserAction" method="save"> <!-- 错误提示:No result defined for action cn.itcast.converter.UserAction and result input 配置如果出错的时候,自动化转向到错误页面 --> <result name="success">/converter/success.jsp</result> <result name="input">/converter/error.jsp</result> </action> </package> </struts>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'userform.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> <form action="${pageContext.request.contextPath}/converter/userAction_save.do" name="form1" method="post"> 编号:<input type="text" name="id"><br/> 姓名:<input type="text" name="userName"><br/> 出生日期:<input type="text" name="birth"><br/> <input type="submit" value="提交"><br/> </form> </body> </html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'error.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> 失败! !!<br> <s:fielderror fieldName="birth"/> </body> </html>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <base href="<%=basePath%>"> <title>My JSP 'success.jsp' starting page</title> <meta http-equiv="pragma" content="no-cache"> <meta http-equiv="cache-control" content="no-cache"> <meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> <meta http-equiv="description" content="This is my page"> <!-- <link rel="stylesheet" type="text/css" href="styles.css"> --> </head> <body> 成功!!!!<br/> </body> </html>
package cn.itcast.converter; import java.util.Date; import com.opensymphony.xwork2.ActionSupport; public class UserAction extends ActionSupport { private static final long serialVersionUID = 1L; /* *在struts2框架中,在对应动作类action中 *声明与页面中表单元素同名的属性,给出对应的set和get方法 *struts2框架就会根据反射机制,获取页面中表单元素的值 */ private Integer id; private String userName; private Date birth;//Unexpected Exception caught setting 'birth' on 'class cn.itcast.converter.UserAction: Error setting expression 'birth' with value ['2012/01/01', ] public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override public String execute() throws Exception { System.out.println("UserAction ****** execute"); return "success"; } public String save() throws Exception { System.out.println("UserAction ****** save"); /* web方式获取: HttpServletRequest request=ServletActionContext.getRequest(); String username=request.getParameter("userName"); System.out.println("username ="+username ); */ System.out.println("id = "+id); System.out.println("userName = "+userName); System.out.println("birth = "+birth); return "success"; } public void setBirth(Date birth) { this.birth = birth; } public Date getBirth() { return birth; } }
package cn.itcast.converter; import java.text.ParseException; import java.text.SimpleDateFormat; import com.opensymphony.xwork2.conversion.impl.DefaultTypeConverter; /* * 自定义转换器 * 作用:把页面中birth元素的字符串内容内容转换成java.util.Date */ public class DateConverter extends DefaultTypeConverter { @Override public Object convertValue(Object value, Class toType) { //要转换的值 [Ljava.lang.String;@27d314cc // System.out.println("value = "+value); //要转换的类型: class java.util.Date //System.out.println("toType = "+toType); if(value==null){ return false; } if(toType==null){ return false; } if(toType!=java.util.Date.class){ return false; } if(value instanceof java.lang.String[]){ String[] strs=(String[]) value; if(strs[0]!=null && !strs[0].trim().isEmpty()){ try { //先确定日期的格式 SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd"); return sdf.parse(strs[0]); } catch (ParseException e) { /* * 在struts2框架里,自定义的类型转换器 * 如果我们不手动抛出异常,struts2框架只捕获异常,但是并不抛出 * 所以框架就会认为类型转换器转换成功,转向成功页面 */ throw new RuntimeException(e); } } } return false; } }
# # Copyright (c) 2002-2006 by OpenSymphony # All rights reserved. # xwork.error.action.execution=Error during Action invocation xwork.exception.missing-action=There is no Action mapped for action name {0}. xwork.exception.missing-package-action=There is no Action mapped for namespace {0} and action name {1}. xwork.default.invalid.fieldvalue=Invalid field value for field "{0}".