请求参数到表述层的类型转换——Struts2
一、简介
说明:HTTP 协议传输数据没有类型的概念,在服务器端是通过 request.getParameter()、request.getParameterValue() 方法得到请求参数为 String 或 String[] 类型。
但是这样使用起来不方便,我们希望开源框架能自动的完成类型转换,到使用的时候能直接获取目标类型。
二、Struts2 类型转换
1. Struts2 本身完成了字符串类型到基本数据类型的自动转换,其他情况需要我们定义自己的类型转换器。
2.类型转换失败时的处理方式
(1) 默认情况下 Struts2 对于类型转换失败后是不做任何处理的。
(2) 若请求参数到目标基本类型转化出错时,会对目标基本类型赋值为默认值。
(3) 若请求参数到目标基本类型包装类转化出错时,会对目标字段赋值为 null。
(4) 若希望在发生错误时给出提示,则需要让目标 Action 类实现 com.opensymphony.xwork2.ValidationAware 接口,这个接口 ActionSupport 已经帮我们实现。
说明:com.opensymphony.xwork2.ValidationAware
包含了 ActionErrors 和 FieldErrors 两个级别的错误消息,实现了该接口的 Action 类,在类型转换出错时,会前往 name=input 的 result,若没有配置,则返回404。
在类型转换出错时,不会到目标方法,而是直接返回。对应的是一个拦截器。
(5) 在错误显示页面,可以从值栈的 Action 类中获取到错误消息,且此错误消息可以自定义。
自定义错误提示消息:在当前 Action 类所在的包下添加 ActionName.properties 的属性文件,然后在该属性文件中添加如下键值对:
invalid.fieldvalue.目标字段名=定制的错误提示消息
(6) 在值栈中,错误消息的数据结构为:Map<String, List<String>>,可以通过 OGNL 表达式的方式来获取,也可以通过 Struts2 标签来获取,如下:
<s:fielderror fieldName="errorFiledName"></s:fielderror>
3.自定义类型转换器
继承 StrutsTypeConverter 类,两个抽象方法
public Object convertFromString(Map context, String[] values, Class toClass) : context 可以看做是 Map 栈,values 代表传入的请求参数, toClass 代表要转换的目标类。
public String convertToString(Map context, Object o) : 将传入的 o 转换为字符串。
(1) 类级别的
在同包下创建包含要转换的属性的类简单类名 + "-conversion.properties", 如:TypeConverterTestAction-conversion.properties,TypeConverterTestAction 类中包含一个 person 属性。
在创建的类型转换资源文件中添加:要转换的属性=类型转换器全类名,如: person=com.nucsoft.struts2.convert.MyTypeConverter
(2) 全局级别的
在类路径的根目录下创建:xwork-conversion.properties 属性文件
内容:需要转换的类的全类名=类型转换器的全类名,如:com.nucsoft.struts2.helloworld.Person=com.nucsoft.struts2.convert.MyTypeConverter
自定义类型转换出错的处理方式也和之前一样。
4.复杂类型数据的类型转换
(1)目标 Action 不实现 ModelDriven 接口,也就是说栈顶对象是当前 Action 类的情况
[1]单独的一个复杂对象,表单标签与对象属性对应:person
<s:form action="converter/typeConverter" method="post"> <s:textfield name="person.userName" label="userName"/> <s:textfield name="person.age" label="age"/> <s:submit value="submit"/> </s:form>
(2) 目标 Action 实现 ModelDriven 接口,栈顶对象为 getModel 返回值类型。
[1] 单独一个复杂类型,对应多个标签
<s:form action="converter/typeConverter" method="post"> <s:textfield name="userName" label="person"/> <s:textfield name="age" label="age"/> <s:submit value="submit"/> </s:form>
[2]单独的一个复杂对象,只有一个表单标签,按照指定格式进行转换为对应的复杂对象:
<s:form action="converter/typeConverter" method="post"> <s:textfield name="person" label="person"/> <s:submit value="submit"/> </s:form>
输入的格式:
AA,12
需要对目标 Action 自定义 person 类型转换器,或定义全局的类型转换器,如:
/** * @author solverpeng * @create 2016-07-09-11:00 */ public class MyTypeConverter extends StrutsTypeConverter { @Override public Object convertFromString(Map context, String[] values, Class toClass) { Person person = null; if(Person.class == toClass) try { person = (Person) toClass.newInstance(); String strs = values[0]; person.setUserName(strs.split(",")[0]); person.setAge(Integer.parseInt(strs.split(",")[1])); } catch(InstantiationException e) { e.printStackTrace(); } catch(IllegalAccessException e) { e.printStackTrace(); } return person; } @Override public String convertToString(Map context, Object o) { return null; } }
[3]复杂对象中包含另一个复杂对象,如 Person 中包含 Address。
对应多个标签:
<s:form action="converter/typeConverter" method="post"> <s:textfield name="userName" label="person"/> <s:textfield name="age" label="age"/> <s:textfield name="address.addressName" label="addressName"/> <s:textfield name="address.location" label="location"/> <s:submit value="submit"/> </s:form>
三个标签,固定格式:address 用一个标签,固定格式 addressName,location
<s:form action="converter/typeConverter" method="post"> <s:textfield name="userName" label="userName"/> <s:textfield name="age" label="age"/> <s:textfield name="address" label="address"/> <s:submit value="submit"/> </s:form>
因为 Person 类中包含 Address 属性,所以在 Person 同包下新建 Person-conversion.properties 文件
内容:address=com.nucsoft.struts2.convert.MyTypeConverter
类型转换器:
/** * @author solverpeng * @create 2016-07-09-11:00 */ public class MyTypeConverter extends StrutsTypeConverter { @Override public Object convertFromString(Map context, String[] values, Class toClass) { if(Person.class == toClass) { Person person = null; try { person = (Person) toClass.newInstance(); String strs = values[0]; person.setUserName(strs.split(",")[0]); person.setAge(Integer.parseInt(strs.split(",")[1])); } catch(InstantiationException e) { e.printStackTrace(); } catch(IllegalAccessException e) { e.printStackTrace(); } return person; } if(Address.class == toClass) { Address address = null; try { address = (Address) toClass.newInstance(); String strs = values[0]; address.setAddressName(strs.split(",")[0]); address.setLocation(strs.split(",")[1]); } catch(InstantiationException e) { e.printStackTrace(); } catch(IllegalAccessException e) { e.printStackTrace(); } return address; } return null; } @Override public String convertToString(Map context, Object o) { return null; } }
5.集合元素对象中的类型转换
[1] 目标 Action 类包含 List 的复杂对象,如 List<Person>,其中 Address 进行类型转换时,还是使用 Person 转换器,且因为为 List<Person>,所以不需要实现 ModelDriven 接口,所以栈顶对象为 当前Action类对象。
<s:form action="converter/typeConverter" method="post"> <s:textfield name="persons[0].userName" label="userName[0]"/> <s:textfield name="persons[0].age" label="age[0]"/> <s:textfield name="persons[0].address" label="address[0]"/> <s:textfield name="persons[1].userName" label="userName[1]"/> <s:textfield name="persons[1].age" label="age[1]"/> <s:textfield name="persons[1].address" label="address[1]"/> <s:submit value="submit"/> </s:form>
[2] 只有一个复杂对象,但是该复杂对象包含 List 属性,如 Person 类存在 List<Address> 属性。因为只有一个复杂对象,所以实现 ModelDriven 接口
<s:form action="converter/typeConverter" method="post"> <s:textfield name="userName" label="userName"/> <s:textfield name="age" label="age"/> <s:textfield name="addresses[0].addressName" label="address[0].addressName" /> <s:textfield name="addresses[0].location" label="address[0].location" /> <s:textfield name="addresses[1].addressName" label="address[1].addressName" /> <s:textfield name="addresses[1].location" label="address[1].location" /> <s:submit value="submit"/> </s:form>
6.json类型的数据的类型转换
未完,待续