開發基于瀏覽器的客戶端總是會遇到表單重復提交的問題。很明顯的在一個商業應用中重復提交是不希望發生的。struts提供一個機制,使用Action基類的generateToken方法產生一個token來保護模型層避免受到表單多次提交的影響。為了控制事務的完整性和原子性,在使用ActionForward選擇下一個視圖前要調用request handler的saveToken方法。saveToken方法調用generateToken方法創建一個唯一標識符,然后用Action.TRANSACTION_TOKEN_KEY保存到session中。表單標簽從session中獲得token然后用Constants.TOKEN_KEY保存到隱藏字段中。在接下來的請求中,request handler通過調用action基類的isTokenValid方法檢查token的合法性。如果該方法返回false,request handler必須實現合適的邏輯處理遇到的問題。一個處理的例子如下所示:
if (!isTokenValid(request)) {
errors.add(ActionErrors.GLOBAL_ERROR,
new ActionError("transaction.token.invalid"));
//errors object is of type ActionErrors
}
isTokenValid同步處理session對象的token來阻止表單重復提交請求。在request handlers中,isTokenValid方法必須跟一個resetToken來從session中移除token;這樣可以保證任何后續請求都將導致isTokenValid返回fa,因此組織表單多次提交。request handler的saveToken方法應該被調用來重建一個新的事務token滿足下次的請求。
獲得表單數據
jsp規范使用
例子映射一個JavaBean類型為packageName.customerProfileForm用name=customerProfileForm (唯一標識),name=customerProfileForm; request handler通過輸入請求中的路徑/editCustomerProfile唯一標識。 表單的創建和使用在下面的圖片中解釋。
用FormTag初始化ActionForm對象
用前面介紹的方式FormTag初始化ActionForm對象,在HTML表單中的action URL對應
用ActionForm存儲表單數據
被繼承的ActionForm對象是用來存放表單請求對象參數的,因此他們是和用戶緊耦合的。一個ActionForm子類是一個帶屬性存取方法的JavaBean,這些屬性與HttpServletRequest對象的參數對應。如果一個ActionForm對象通過FormTag創建(前面介紹了),那么在接下來通過FormTag解析的表單請求將由RequestProcessor在特定范圍處理。表單通過相關的action映射標識找到。RequestProcessor為重置表單屬性,通常表單帶有請求時間參數,然后調用表單對象的validate方法完成服務端校驗用戶輸入。僅當ActionMapping對象的校驗屬性設為true時,validate方法才會被調用。這是一個默認的行為。使用request.getParameterValues(parameterName)獲得一個String數組對象,這個對象用來存放表單數據;在請求參數類型轉換一節將做解釋。校驗結構是一個ActionErrors對象(在錯誤處理一節解釋了),它使用org.apache.struts.taglib.html.ErrorsTag顯示校驗錯誤給用戶。ActionForm也能存儲中間層模型狀態,這些狀態數據接下來會視圖(jsp)引用并展示給用戶。
一個ActionForm類也可以被RequestProcessor創建。這種情況發生在導航到URL,該URL映射到控制器servlet而不是jsp,相關的action映射定義了表單屬性。在這種情況下,RequestProcessor搜索form bean的嘗試會導致創建一個新的ActionForm對象,如果在特定范圍沒有找到的話,或者在特定范圍通過
表單對象的創建的目的是為了提供中間層模型狀態給jsp時應該使用請求范圍;這樣可以保證在他們的有效性過期后對象不會再被使用。默認的所有表單保存在session范圍。在session中存在的表單對象超過了他們的有效性范圍會導致浪費內存,因此,請求處理者必須跟蹤存儲在session中的表單對象的生命周期。獲得表單數據的一個好的實踐是用單一的form bean關聯跨越服務端用戶交互的表單。form beans也能夠存儲中間層模型狀態,這個狀態在相應用戶時,通過定制的標簽適配到用戶視圖中。標簽用來避免在視圖層混合java代碼,因此導致了一個好的分工,web開發團隊處理標記,應用開發團隊處理java代碼。標簽區分了中間層處理邏輯;這些邏輯可能相當復雜,比如處理嵌套對象或通過集合迭代對象。
用動態屬性創建ActionForm對象
DynaActionForm對象是帶動態集合屬性的對象。DynaActionForm擴展了ActionForm對象;通過在struts-config.xml文件中聲明來創建form對象。例子略。RequestProcessor創建,操作,校驗DynaActionForm的方式和操作ActionForm是一致的,也就是說對于在DynaActionForm的動態屬性(定義在
請求參數類型轉換
這節討論request.getParameterValues(parameterName)獲得的String數組類型怎樣轉換到表單對象的對應屬性類型上。下面是支持的目標類型列表。
java.lang.BigDecimal
java.lang.BigInteger
boolean and java.lang.Boolean
byte and java.lang.Byte
char and java.lang.Character
double and java.lang.Double
float and java.lang.Float
int and java.lang.Integer
long and java.lang.Long
short and java.lang.Short
java.lang.String
java.sql.Date
java.sql.Time
java.sql.Timestamp
目標類型,也就是說和表單對象屬性關聯的類型,是通過自省機制找到的;一個特定struts定制的自省機制是在DynaActionForm對象中使用的。struts也支持索引屬性,parameterName[n];索引從0開始。這種命名轉換的form bean的方法是通過按照下面定義的javabeans規范描述的,使用了索引屬性模式。下面的方法用來處理所有的索引屬性的數組元素。
public
public void set
下面的方法是用來處理單獨的數組元素
public
public void set
下面描述了使用簡單的屬性和索引屬性的語法。
1.當bean的屬性是數組,且請求參數名沒有使用索引符號parameterName[n]時,由request.getParameterValues(parameterName)返回的String[]將轉換成目標組件類型的數組。由struts框架內部實現該方法。ActionForm子類應該定義下面的方法聲明。
public void set
public
2.當bean的屬性是數組,且請求參數名使用索引符號parameterName[n],由request.getParameterValues(parameterName)返回的String[]將被認為僅容納一個簡單的值;僅String[0]被轉換成數組的組件類型。由struts框架內部實現該方法。ActionForm子類應該定義下面的方法聲明來接收索引參數。
public void set
public
上面的方法聲明遵循了javabeans規范中定義的索引屬性設計模式。當這些方法都沒有時,通過實現下面的方法也可以處理索引存取屬性。
public
在這個場景中,請求數組的讀寫是通過struts框架處理,首先獲得根本的數組對象然后處理給定序號的元素。這個模式也支持基于list的請求參數實現,使用parameterName[n]訪問。
3.對于簡單的屬性類型,由request.getParameterValues(parameterName)返回的String[]將被認為僅容納一個簡單的值;僅String[0]被轉換成目標類型。由struts框架內部實現該方法。對于簡單的屬性,ActionForm子類應該定義下面的方法聲明。
public void set
public
struts綁定的標簽庫提供了處理簡單屬性和索引屬性的方式;org.apache.struts.taglib.nested包容納了這些標簽。struts中的完全的標簽列表請參考http://jakarta.apache.org/struts/userGuide/index.html; 更多的資源請參考http://jakarta.apache.org/struts/resources/index.html. struts將來的趨勢是采用JSTL和
JSF上。