概述
從一個 HTML 表單到一個 Action 對象, 類型轉換是從字符串到非字符串.
HTTP 沒有 “類型” 的概念. 每一項表單輸入只可能是一個字符串或一個字符串數組. 在服務器端, 必須把 String 轉換爲特定的數據類型
在 struts2 中, 把請求參數映射到 action 屬性的工作由 Parameters 攔截器負責, 它是默認的 defaultStack 攔截器中的一員**. Parameters** 攔截器可以自動完成字符串和基本數據類型之間轉換.
類型轉換錯誤
若 Action 類沒有實現 ValidationAware 接口: Struts 在遇到類型轉換錯誤時仍會繼續調用其 Action 方法, 就好像什麼都沒發生一樣.
若 Action 類實現 ValidationAware 接口:Struts 在遇到類型轉換錯誤時將不會繼續調用其 Action 方法: Struts 將檢查相關 action 元素的聲明是否包含着一個 name=input 的 result. 如果有, Struts 將把控制權轉交給那個 result 元素; 若沒有 input 結果, Struts 將拋出一個異常
例子:
//index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<s:form action="testConversion">
<s:textfield name="age" label="Age"></s:textfield>
<s:submit></s:submit>
</s:form>
</body>
</html>
//success.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
Insert title here
</body>
</html>
testConversion.java//
package product;
import com.opensymphony.xwork2.ActionSupport;
public class testConversion extends ActionSupport{
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String execute(){
System.out.println("age"+age);
return "success";
}
}
//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">
<struts>
<package name="hello" extends="struts-default" >
<action name="testConversion" class="product.testConversion">
<result>/success.jsp</result>
<result name="input">/index.jsp</result>
</action>
</package>
</struts>
類型轉換錯誤消息的定製
作爲默認的 default 攔截器的一員, ConversionError 攔截器負責添加與類型轉換有關的出錯消息(前提: Action 類必須實現了 ValidationAware 接口)和保存各請求參數的原始值.
若字段標籤使用的不是 simple 主題, 則非法輸入字段將導致一條有着以下格式的出錯消息:
覆蓋默認的出錯消息
在對應的 Action 類所在的包中新建 ActionClassName.properties 文件, ClassName 即爲包含着輸入字段的 Action 類的類名
在屬性文件中添加如下鍵值對:
如果是theme=“simple”的話,index.jsp改善
<s:debug></s:debug>
<s:form action="testConversion" theme="simple">
<s:textfield name="age" label="Age"></s:textfield>
${fieldErrors.age[0] }
<s:fielderror name="age"></s:fielderror>
<s:submit></s:submit>
</s:form>
問題:
若是 simple 主題, 且使用 <s:fielderror fieldName=“age”></s:fielderror> 來顯示錯誤消息, 則該消息在一個 ul, li, span 中. 如何去除 ul, li, span 呢 ?
在struts-2.3.37-all\struts-2.3.37\apps\struts2-blank\WEB-INF\lib\struts2-core-2.3.37.jar\template.simple 下面的 fielderror.ftl 定義了 simple 主題下, s:fielderror 標籤顯示錯誤消息的樣式. 所以修改該配置文件即可. 在 src 下新建 template.simple 包, 新建 fielderror.ftl 文件, 把原生的 fielderror.ftl 中的內容
複製到新建的 fielderror.ftl 中, 然後剔除 ul, li, span 部分即可.
具體操作28:00
定製出錯消息的樣式:
每一條出錯消息都被打包在一個 HTML span 元素裏, 可以通過覆蓋其行標爲 errorMessage 的那個 css 樣式來改變出錯消息的格式.
顯示錯誤消息: 如果是 simple 主題, 可以通過 <s:fielderror fieldName=“filedname”></s:fielderror> 標籤顯示錯誤消息
定製類型轉換器
index.jap改進
<s:form action="testConversion" theme="simple">
Age: <s:textfield name="age" label="Age"></s:textfield>
${fieldErrors.age[0] }
^<s:fielderror fieldName="age"></s:fielderror>
<br><br>
Birth: <s:textfield name="date"></s:textfield>
<s:fielderror fieldName="date"></s:fielderror>
<br><br>
<s:submit></s:submit>
</s:form>
自定義類型轉換器必須實現 ongl.TypeConverter 接口或對這個接口的某種具體實現做擴展
I. 開發類型轉換器的類: 擴展 StrutsTypeConverter 類.
package product;
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;
import org.apache.struts2.util.StrutsTypeConverter;
public class DateConverter extends StrutsTypeConverter {
private DateFormat format1;
public DateConverter() {
System.out.println(1);
format1=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
}
@Override
public Object convertFromString(Map arg0, String[] arg1, Class arg2) {
System.out.println(2);
// TODO Auto-generated method stub
if(arg2==Date.class)
if(arg1!=null&&arg1.length>0)
try {String a=arg1[0];
return format1.parseObject(a);
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return arg1;
}
@Override
public String convertToString(Map arg0, Object arg1) {
// TODO Auto-generated method stub
System.out.println(3);
if(arg1 instanceof Date){
Date date = (Date) arg1;
return format1.format(date);
}
//若轉換失敗返回 null
return null;
}
}
II. 配置類型轉換器:
有兩種方式
①. 基於字段的配置:
> 在字段所在的 Model(可能是 Action, 可能是一個 JavaBean) 的包下, 新建一個 ModelClassName-conversion.properties 文件
> 在該文件中輸入鍵值對: fieldName=類型轉換器的全類名.
> 第一次使**用該轉換器時創建實例**.
> 類型轉換器是單實例的!
如果model分開
名字改Customer-conversion.properties並在一個包中
②. 基於類型的配置:
> 在 src 下新建 xwork-conversion.properties
> 鍵入: 待轉換的類型=類型轉換器的全類名.
> 在當前 **Struts2 應用被加載時創建實例**.
類型轉換與複雜對象配合使用
form 標籤的 name 屬性可以被映射到一個屬性的屬性.
在上面的基礎上修改
index.jsp
<s:form action="testConversion" theme="simple">
Age: <s:textfield name="age" label="Age"></s:textfield>
${fieldErrors.age[0] }
^<s:fielderror fieldName="age"></s:fielderror>
<br><br>
Birth: <s:textfield name="he.date"></s:textfield>
<s:fielderror fieldName="he.date"></s:fielderror>
<br><br>
<s:submit></s:submit>
</s:form>
////////////////////////////////////////////////////////////
package product;
import java.util.Date;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
public class testConversion extends ActionSupport implements ModelDriven<xin>{
public String execute(){
System.out.println("xin"+x);
return "success";
}
private xin x;
@Override
public xin getModel() {
// TODO Auto-generated method stub
x=new xin();
return x;
}
}
/////////////////////////////////////////////////////
package product;
import java.util.Date;
public class xin {
private int age;
private He he;
public int getAge() {
return age;
}
public He getHe() {
return he;
}
public void setHe(He he) {
this.he = he;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "xin [age=" + age + ", he=" + he + "]";
}
}
////////////////////////////////////////////////////
package product;
import java.util.Date;
public class He {
private Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public He(Date date) {
super();
this.date = date;
}
@Override
public String toString() {
return "He [date=" + date + "]";
}
public He() {
super();
}
}
消息處理與國際化
在程序設計領域, 把在無需改寫源代碼即可讓開發出來的應用程序能夠支持多種語言和數據格式的技術稱爲國際化.
與國際化對應的是本地化, 指讓一個具備國際化支持的應用程序支持某個特定的地區
Struts2 國際化是建立在 Java 國際化基礎上的:爲不同國家/語言提供對應的消息資源文件,Struts2 框架會根據請求中包含的 Locale 加載對應的資源文件通過程序代碼取得該資源文件中 指定 key 對應的消息
配置國際化資源文件
1.Action 範圍資源文件:
在Action類文件所在的路徑建立名爲ActionName_language_country.properties 的文件
2.包範圍資源文件:
在包的根路徑下建立文件名爲package_language_country.properties 的屬性文件,一旦建立,處於該包下的所有 Action 都可以訪問該資源文件。注意:包範圍資源文件的 baseName 就是package,不是Action所在的包名。
3.全局資源文件
命名方式: basename_language_country.properties
struts.xml
struts.propertiesstruts.custom.i18n.resources=baseName
4.臨時指定資源文件:
<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 類中訪問國際化資源文件的 value 值
I. 在 Action 類中. 若 Action 實現了 TextProvider 接口, 則可以調用其 getText() 方法獲取 value 值
> 通過繼承 ActionSupport 的方式。
II. 頁面上可以使用 s:text 標籤; 對於表單標籤可以使用表單標籤的 key 屬性值
> 若有佔位符, 則可以使用 s:text 標籤的 s:param 子標籤來填充佔位符
> 可以利用標籤和 OGNL 表達式直接訪問值棧中的屬性值(對象棧 和 Map 棧)
總之
JSP 頁面訪問國際化消息:
不帶佔位符:
<s:text name=“key”/>
表單元素的 label 屬性:可替換爲 key 或使用 getText() 方法,並對其進行強制 OGNL 解析
帶佔位符:
在 <s:text…/> 標籤中使用多個 <s:param…/> 標籤來填充消息中的佔位符。
Struts2 直接在國際化消息資源文件中通過 “${}” 使用表達式,該表達式將從值棧中獲取對應的屬性值
Action 訪問國際化消息:
若 Action 類繼承了 ActionSupport ,則可調用 TextProvider 接口的 getText 方法。
例子:
//index.jsp
<%@page import="java.util.Date"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<s:debug></s:debug>
<s:form action="">
<!--
若 label 標籤使用 %{getText('username')} 的方式就也可以從國際化資源文件中獲取 value 值了
因爲此時在對象棧中有 DefaultTextProvider 的一個實例, 該對象中提供了訪問國際化資源文件的 getText() 方法
同時還需要通知 struts2 框架 label 中放入的不再是一個普通的字符串, 而是一個 OGNL 表達式. 所以使用 %{} 把 getText()
包裝起來, 以強制進行 OGNL 解析.
-->
<s:textfield name="username" label="%{getText('username')}"></s:textfield>
<!-- key 的方式是直接上資源文件中獲取 value 值 -->
<s:textfield name="password" key="password"></s:textfield>
<s:submit name="submit" key="submit"></s:submit>
</s:form>
</body>
</html>
//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">
<struts>
<constant name="struts.custom.i18n.resources" value="i18n"></constant>
<package name="hello" extends="struts-default" >
</package>
</struts>
資源文件
如果主題是simple的話,i18n.jsp改成
<s:form action="" theme="simple">
<!--
頁面上可以直接使用 <s:text name="" /> 標籤來訪問國際化資源文件裏的 value 值.
-->
<s:text name="username"></s:text>
<s:textfield name="username" label="%{getText('username')}"></s:textfield><br/>
<s:text name="password"></s:text><s:textfield name="password" key="password"></s:textfield><br/>
<s:submit name="submit" key="submit" value="%{getText('submit')}"></s:submit>
</s:form>
Action 類中訪問國際化資源文件的 value 值
package product;
import java.util.Arrays;
import java.util.Date;
import com.opensymphony.xwork2.ActionSupport;
public class TestI18nAction extends ActionSupport {
/**
*
*/
private static final long serialVersionUID = 1L;
private Date date = null;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@Override
public String execute() throws Exception {
date = new Date();
//1. 在 Action 中訪問國際化資源文件的 value 值
String username = getText("username");
System.out.println(username);
//2. 帶佔位符的
String time = getText("time", Arrays.asList(date));
System.out.println(time);
return SUCCESS;
}
}
在i18n.jsp加入
<s:text name="time"><s:param value="date"></s:param>
</s:text>
<s:text name="time2"></s:text>
利用超鏈接實現動態加載國際化資源文件
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 對象。
例子:
i18n.jsp加入以下
<a href="testI18n.action?request_locale=en_US">English</a>
<a href="testI18n.action?request_locale=zh_CN">中文</a>