B/S結構的軟件開發中,特別是在越大型的分布式應用中體現的越明顯,后端的處理往往會因為出現較多的時間消耗而引起延遲,這種延遲有可能過長而最終使用戶認為是自己的操作錯誤,導致他們重新提交請求,由于任務的重復提交,服務器資源大部分被占用,情節嚴重可能出現類似死機現象。
預期達到目標:
?? ?1、當用戶進行的是Refresh/Reload/Back/Forward操作、以及先Back再Submit操作時,僅僅是reloading先前的結果頁。
?? ?2、當用戶重復提交同一個任務操作時,后臺服務接收并處理第一次提交的任務,后面提交不起作用(不轉向也不提示)。
?? ?3、該功能具有公用性。
基本形成思路:
?? ?1、在basic filter中實現公用性
?? ??? ?if(true){//問題1:如何確定是否為重復提交
?? ??? ??? ?...
?? ??? ??? ?chain.doFilter(request,response);
?? ??? ?}else{
?? ??? ??? ?//問題2:如何實現不轉向、不提示也不顯示空白頁
?? ??? ?}
?? ?
?? ?2、網上資料概括
?? ??? ?a、提交表單后按鈕變灰/隱藏提交按鈕
?? ??? ?b、在js里設置全局變量,提交后修改該變量的值,依據變量的值判斷是否重復提交
??????? var flag=true;
??????? function checkForm(){
??????????? if (flag==false){
??????????????? return;
??????????? }
??????????? flag=false;
??????????? document.form1.submit();???????????????????? ?
??????? }
?? ??? ?c、struts (webwork沒有找到這個資料)
??????? 驗證事務控制令牌,<html:form >會自動根據session中標識生成一個隱含input代表令牌,防止兩次提交
??????? 在action中:????????????????????? ?
??????? //<input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae">????????????????????? ?
??????? if (!isTokenValid(request))
??????????? errors.add(ActionErrors.GLOBAL_ERROR,new ActionError("error.transaction.token"));
??????? resetToken(request); //刪除session中的令牌
??????? action有這樣的一個方法生成令牌華
??????? protected String generateToken(HttpServletRequest request) {
??????????? HttpSession session = request.getSession();
??????????? try {
??????????????? byte id[] = session.getId().getBytes();
??????????????? byte now[] = new Long(System.currentTimeMillis()).toString().getBytes();
??????????????? MessageDigest md = MessageDigest.getInstance("MD5");
??????????????? md.update(id);
??????????????? md.update(now);
??????????????? return (toHex(md.digest()));
??????????? } catch (IllegalStateException e) {
??????????????? return (null);
??????????? } catch (NoSuchAlgorithmException e) {
??????????????? return (null);
??????????? }
??????? } ?
?????????????????? ?
??? ??? d、用戶使用瀏覽器時,可以經常使用向后的按鈕,因此就有可能重復提交一個他們已經提交過的form,這樣就會帶來一個重復事務處理的問題。同樣,一個用戶也可能在接收到一個確認的頁面之前按下停止的按鈕,接著再次提交同一個form。對于這些情況,我們都想跟蹤并且禁止這些重復的提交,我們可以使用一個控制servlet來提供一個控制點,以解決這個問題。
??????? 同步記號(Synchronizer (or Dvu) Token)
??????? 這個策略是為了解決重復的form提交問題。一個同步的記號被設置在一個用戶的Session中,并且包含在返回到客戶的每一個form中。當form 被提交時,form中的同步標記就和Session中的同步標記作對比。在form首次提交的時候,這兩個標記應該是一樣的。如果標記不一樣,那么該 form就會禁止提交,一個錯誤就會返回給用戶。在用戶提交一個form時,如果按下瀏覽器中的后退按鈕并嘗試重新提交同一個form時,標記就會出現不匹配的現象。
??????? 另一方面,如果兩個標記值匹配,那么我們就可以確信整個流程是正確的。在這種情況下,Session中的標記值就會被修改為一個新的值,同時允許提交該form。
??????? 你也可以使用這個策略來控制對某些頁面的直接訪問,就好象上面資源保護中描述的一樣。例如,假設一個用戶將某個應用的頁面A收藏到收藏夾中,而頁面A只允許通過頁面B和C訪問。當用戶直接通過收藏夾來訪問頁面A,這時頁面的訪問順序就是不正確的,這樣同步標記將處在一個不同步的狀態,或者它根本就不存在。不論怎樣,訪問都被禁止了。
?? ?
??? ??? e、做一個hidden框,名字自己定,提交后得到這個值放入session,提交前判斷session是否為空。
??? ??? f、另Struts提供利用Token防止重復提交的方法:
??????? 用戶請求錄入頁面,這個與服務器建立的一次連接過程中,在服務器端①【生成一個session標識,同時返回到客戶端一個與此匹配的hidden域】。用戶提交了此頁面,服務器端首先②【判斷此hidden域與session標識是否匹配】,若不匹配,終止保存操作,提示同一表單不能提交兩次,同時①【新建一個session標識和hidden域】,返回錄入頁面;若匹配,執行插入保存操作,同時③【清空(重置reset)session標識】。
??? ??? Struts正在基于這樣的思路在org.apache.struts.action.Action類中提供了內置支持方法:
??? ??? java代碼:
?? ??? ??? ??? ?
??? ??? ??? protected void saveToken(HttpServletRequest request) 配合標簽對應于①
??? ??? ??? protected boolean isTokenValid(HttpServletRequest request) 對應于②
??? ??? ??? protected void resetToken(HttpServletRequest request) 對應于③
?? ??? ??? ??? ?
??? ??? 這樣我們在寫程序的時候,結合Struts的html標簽,只要
??? ??? ??? 1.在forward到insert.jsp頁面前加一個action執行saveToken(request)操作,或干脆在insert.jsp中寫
??? ??? ??? 2.保存前加個判斷操作isTokenValid(request)
??? ??? ??? 3.若isTokenValid(request)返回false,執行saveToken(request)操作,返回錯誤提示頁面;true則執行resetToken(request)操作,然后進行實際的保存操作
解決方案:
?? 1、后臺公共類中實現前臺的Form中自動生成兩個hidden文本功能,一個是作page是否重復提交判斷,并由系統自動附上關鍵值(如struts采用的方案);另一個作為button是否重復提交判斷(struts中好像沒有)。由后臺公共類實現界面兩個hidden text自動生成的好處在于公用性。
?? 2、在basic filter中根據兩個hidden text值判斷是否為重復提交。
?? 3、javascript中作一個公共方法,實現功能:如果需要判斷是否重復提交,就給第二個hidden text附上關鍵值,并使該功能不可用。