7.类型转换、消息处理与国际化
@(10_Struts2)[JavaEE, Struts2]
- 前边我们说过了 Http协议 有两个缺陷一个是无状态、一个是纯文本。
- 纯文本也就是说Http请求中的内容都是以字符串的形式发送的。
- 但是Java又是一个强类型语言,所以将一个字符串转换成Java中的数据类型这一重任就落在了Struts2的肩膀上。
- 类型转换最常见的可能就是将将10/29/2015转换为一个date类型等。
- 关于类型转换我们要做的主要有两件事:
- 类型转换失败后的处理。
- 特殊类型的自定义类型转换器。
类型转换失败消息处理
-
类型转换失败:
若 Action 类没有实现 ValidationAware 接口: Struts 在遇到类型转换错误时仍会继续调用其 Action 方法, 就好像什么都没发生一样.
-
若 Action 类实现 ValidationAware 接口:Struts 在遇到类型转换错误时将不会继续调用其 Action 方法:
- Struts 将检查相关 action 元素的声明是否包含着一个 name=input 的 result.
- 如果有, Struts 将把控制权转交给那个 result 元素;
- 若没有 input 结果, Struts 将抛出一个异常
实现
ValidationAware
接口,在之前的文章中我们提到,我们可以继承com.opensymphony.xwork2.ActionSupport
类的形式来实现ValidationAware
接口, 在ActionSupport 中已经帮我们实现好了
-
类型转换出错时如何处理
- 如果我们输入错误的数字,想转换为年龄;当我们的 result name="input" 的时候,会返回到这个结果页面。
- 但是对于这种错误的提示,我们需要处理
-
类型转换错误消息的定制
- 作为默认的 default 拦截器的一员, ConversionError 拦截器负责添加与类型转换有关的出错消息(前提: Action 类必须实现了 ValidationAware 接口)和保存各请求参数的原始值.
- 若字段标签使用的不是 simple 主题, 则非法输入字段将导致一条有着如上图格式的出错消息。
-
覆盖默认的出错消息
- 在对应的
Action
类所在的包中新建ActionClassName.properties
文件, ClassName 即为包含着输入字段的 Action 类的类名 - 在属性文件中添加如下键值对: invalid.fieldvalue.fieldName=Customer Message
- 在对应的
# EmployeeAction.properties
invalid.fieldvalue.age=年龄格式不正确,请重新输入!
-
定制出错消息的样式:
- 每一条出错消息都被打包在一个 HTML span 元素里, 可以通过覆盖其行标为 errorMessage 的那个 css 样式来改变出错消息的格式.
- 显示错误消息: 如果是 simple 主题, 可以通过
<s:fielderror fieldName=“filedname”></s:fielderror>
标签显示错误消息
<s:form action="emp_update" theme="simple">
<s:hidden name="id"></s:hidden>
姓名:<s:textfield name="name"></s:textfield> <br>
年龄:<s:textfield name="age" ></s:textfield>
<!-- 通过标签获取错误消息 -->
<span style="color:red"><s:fielderror fieldName="age"></s:fielderror></span><br>
部门: <s:textfield name="dept"></s:textfield><br>
职务:<s:textfield name="role"></s:textfield><br>
<s:submit value="修改"></s:submit>
</s:form>
- 但是对于这种错误消息的格式我们是可以修改的,修改其默认的 freemark 模板
- 在struts2-core-2.3.15.3.jar 的 /template/simple/fielderror.ftl,这是 struts2 默认自带的错误消息模板,我们在 src 路径下建立相同的包和模板,并且修改里面的 html 标签。删除文件中错误消息的 的 ul、li、span便签
![删除的模板
自定义类型转换器
定制类型转换器
-
自定义类型转换器必须实现
ongl.TypeConverter
接口或对这个接口的某种具体实现做扩展 -
扩展 StrutsTypeConverter 类
- 在大多数类型转换器里, 需要提供从 String 类型到非 String 类型和与此相反的转换功能
- 在 StrutsTypeConverter 中有两个抽象方法:
-
配置自定义的类型转换器
- 在应用程序里使用一个自定义的类型转换器之前, 必须先对它进行配置. 这种配置既可以基于字段, 也可以基于类型
-
基于字段类型转换配置-局部转换器
- 可以为某个 Model(该 Model 类也可能是 Action) 的各个属性分别配置一个自定义的转换器.
- 创建一个属性文件: ModelClassName-conversion.properties, 该文件需和相对应的 Model(Model有可能是一个Action) 类放在同一个目录下
- fieldName=Converter全类名,例如:
birth=org.pan.struts2.conver.DateConverter
-
基于类型-全局转换器
- 在资源目录下,src 或者 其他的 resources 创建
xwork-conversion.properties
文件 - 在 xwork-conversion.properties 文件里把每一个需要进行类型转换的类与一个类型转换器关联起来
java.util.Date=org.pan.struts2.conver.DateConverter
- 若类型转换失败,给出自定义的信息。
- 在资源目录下,src 或者 其他的 resources 创建
复杂属性,集合协同工作
- 复杂属性
- 类型转换与复杂属性配合使用
- form 标签的 name 属性可以被映射到一个属性的属性.
<s:form action="user_add">
<s:textfield name="user.name" label="姓名"></s:textfield>
<s:textfield name="user.gender" label="性别"></s:textfield>
<s:textfield name="user.address" label="地址"></s:textfield>
<s:textfield name="user.mgr.name" label="经理名称"></s:textfield>
<s:textfield name="user.mgr.birth" label="经理生日"></s:textfield>
<s:submit value="添加"></s:submit>
</s:form>
-
集合协同
- 类型转换与 Collection 配合使用
国际化
-
概述
- 在程序设计领域, 把在无需改写源代码即可让开发出来的应用程序能够支持多种语言和数据格式的技术称为国际化.
- 与国际化对应的是本地化, 指让一个具备国际化支持的应用程序支持某个特定的地区
- Struts2 国际化是建立在 Java 国际化基础上的:
- 为不同国家/语言提供对应的消息资源文件
- Struts2 框架会根据请求中包含的
- Locale 加载对应的资源文件
- 通过程序代码取得该资源文件中
- 指定 key 对应的消息
- Struts2 框架会根据请求中包含的
-
默认拦截器栈中有一个
i18n拦截器
,这个拦截器用来设置当前Locale
信息- 当i18n拦截器被调用时,他会先去查找request_locale这个请求参数,它会根据干参数来创建一个Locale对象。并将Locale对象放入进session域中。之后在加载国际化资源文件时就根据刚刚创建的Locale对象
当i18n拦截的请求中没有request_locale这个参数,
它会先去session中获取Locale对象,如果session中,则直接使用该对象
如果session中没有,则会根据用户浏览器信息或本地服务器的信息去创建一个Locale对象并使用。
-
配置国际化资源文件
-
Action 范围资源文件:
- 在Action类文件所在的路径建立名为
ActionName_language_country.properties
的文件
- 在Action类文件所在的路径建立名为
-
包范围资源文件:
- 在包的根路径下建立文件名为
package_language_country.properties
的属性文件,一旦建立,处于该包下的所有 Action 都可以访问该资源文件。 - 注意:包范围资源文件的 baseName 就是package,不是Action所在的包名。
- 在包的根路径下建立文件名为
- 全局资源文件
- 命名方式:
basename_language_country.properties
-
struts.xml
<constant name="struts.custom.i18n.resources" value="baseName"/>
- 命名方式:
-
Action 范围资源文件:
<!-- 配置国际化资源 -->
<constant name="struts.custom.i18n.resources" value="i18n"/>
- 临时指定资源文件:
<s:i18n.../>
标签的name
属性指定临时的国际化资源文件
- 总结:
- 国际化资源文件加载的顺序如何呢 ?
- 离当前 Action 较近的将被优先加载.
- 假设我们在某个
ChildAction
中调用了getText("username")
:
- (1)加载和 ChildAction 的类文件在同一个包下的系列资源文件 ChildAction.properties
- (2)加载 ChildAction 实现的接口 IChild,且和 IChildn 在同一个包下 IChild.properties 系列资源文件。
- (3)加载 ChildAction 父类 Parent,且和 Parent 在同一个包下的 baseName 为 Parent.properties 系列资源文件。
- (4) 若 ChildAction 实现 ModelDriven 接口,则对于getModel()方法返回的model 对象,重新执行第(1)步操作。
- (5) 查找当前包下 package.properties 系列资源文件。
- (6) 沿着当前包上溯,直到最顶层包来查找 package.properties 的系列资源文件。
- (7) 查找 struts.custom.i18n.resources 常量指定 baseName 的系列资源文件。
- (8) 直接输出该key的字符串值。
- Action 范围国际化
- package 范围国际化
- 全局的国际化
- 首先需要去 struts.xml 中指定baseName
<!-- 指定国际化 baseName, 以便于建立全局的国际化资源文件 -->
<constant name="struts.custom.i18n.resources" value="myI18n"></constant>
-
在这还需要讲到一种方式:在页面国际化资源
- 如果我们在资源路径下建立好了 国际化资源,但是没有在 struts.xml 中配置 baseName
- 我们可以使用
<s:i18n name="baseName"> 标签
来获取
<!-- 如果是配置了页面国际化资源文件,
那么国际的内容只能在i18n标签中使用,一旦离开i18n标签则不能使用 -->
<s:i18n name="myI18n">
<s:text name="username"></s:text>
<s:form>
<s:textfield name="username" key="username"></s:textfield>
<s:password name="password" key="password"></s:password>
<s:submit key="submit"></s:submit>
</s:form>
</s:i18n>
-
访问国际化消息
-
JSP 页面访问国际化消息:
- 不带占位符:
<s:text name="key"/>
- 表单元素的 label 属性:可替换为 key 或使用 getText() 方法,并对其进行强制 OGNL 解析,使用%{}
- 带占位符:
- 在 <s:text.../> 标签中使用多个 <s:param.../> 标签来填充消息中的占位符。
- Struts2 直接在国际化消息资源文件中通过 “${}” 使用表达式,该表达式将从值栈中获取对应的属性值(对象栈 和 Map 栈)
- 不带占位符:
-
Action 访问国际化消息:
- 若 Action 类继承了 ActionSupport ,则可调用 TextProvider 接口的 getText 方法。
-
-
利用超链接实现动态加载国际化资源文件
- Struts2 使用 i18n 拦截器 处理国际化,并且将其注册在默认的拦截器中
- i18n拦截器在执行Action方法前,自动查找请求中一个名为
request_locale 的参数
。 - 如果该参数存在,拦截器就将其作为参数,转换成Locale对象,并将其设为用户默认的
Locale
(代表国家/语言环境)。并把其设置为 session 的 WW_TRANS_I18N_LOCALE 属性 - 若
request 没有名为request_locale 的参数,则 i18n 拦截器会从 Session 中获取 WW_TRANS_I18N_LOCALE 的属性值
,若该值不为空,则将该属性值设置为浏览者的默认Locale - 若 session 中的 WW_TRANS_I18N_LOCALE 的属性值为空,则从 ActionContext 中获取 Locale 对象。
<!-- 必須是 struts 請求,使得 i18 拦截器工作 -->
<a href="userLogin.action?request_locale=en_US">English</a>
<a href="userLogin.action?request_locale=zh_CN">中文</a>