轉(zhuǎn)至:http://www./articles/6FBZbmf 我們大家再進(jìn)行web開發(fā)的時(shí)候,必不可少會(huì)遇見表單重復(fù)提交問題,。今天就來給總結(jié)如何解決表單提交問題,,歡迎大家交流指正。 首先我們在討論如何解決表單重復(fù)提交問題之前先來解決三個(gè)問題:1.什么叫表單重復(fù)提交,?2.什么情況下會(huì)出現(xiàn)表單重復(fù)提交?3.什么情況需要避免表單重復(fù)提交,? 什么叫表單提交問題,,說白了,就是同一份信息,,重復(fù)的提交給服務(wù)器,。 那么,在什么情況下回產(chǎn)生表單重復(fù)提交的情況呢,? 給大家列舉以下情況: 1.點(diǎn)擊F5刷新頁面:當(dāng)用戶點(diǎn)擊submit將已經(jīng)寫好的表單數(shù)據(jù)提交到服務(wù)器時(shí),,可以在瀏覽器的url看到地址和參數(shù)的變化,但因?yàn)榫W(wǎng)速等問題,用戶當(dāng)前頁面并未刷新,,或者點(diǎn)擊刷新頁面,,造成表單重復(fù)提交。 2.重復(fù)點(diǎn)擊提交按鈕:因?yàn)榫W(wǎng)絡(luò)問題,,未能及時(shí)跳轉(zhuǎn)顯示內(nèi)同,,部分用戶可能會(huì)出于心急重復(fù)提交提交按鈕,造成多次提交內(nèi)容到服務(wù)器,。 3.前進(jìn)后退操作:有些用戶在進(jìn)行某些工作操作時(shí),,可能出于需要或者某種情況,進(jìn)行后退操作,,瀏覽剛才填入的信息,,在進(jìn)行后退和前進(jìn)的操作可能也會(huì)造成表單數(shù)據(jù)重復(fù)提交到服務(wù)器。 4.使用瀏覽器歷史記錄重復(fù)訪問:某些用戶可能會(huì)出于好奇,,利用瀏覽器的歷史記錄功能重復(fù)訪問提交頁面,,同樣會(huì)造成表單重復(fù)提交問題。 。 ,。 ,。 其實(shí)我們在進(jìn)行web開發(fā)的時(shí)候,遇到的所有問題不一定是都需要解決的,,大多數(shù)問題需要分情況而定,。 例如現(xiàn)在所說的表單重復(fù)提交問題 那什么情況下必須防止表單重復(fù)提交問題呢:例如說注冊用戶功能(向數(shù)據(jù)庫中寫入垃圾信息),投票功能(多次刷票),,購物結(jié)算功能(多次扣錢),,防止論壇灌水功能。,。,。。 明確了以上三個(gè)問題后,,我們來討論如何解決表單重復(fù)提交問題: 翻閱了眾多博客發(fā)現(xiàn)表單重復(fù)問題可以有多種解決方案,,總結(jié)如下: 1.js方法解決:關(guān)于js方法解決就是說通過js動(dòng)態(tài)控制提交按鈕不能多次點(diǎn)擊,或者多次點(diǎn)擊不起作用,。 方案一:通過設(shè)立標(biāo)識(shí)使表單不能重復(fù)提交: var flag=true; function Sub(){ if(flag){ flag = false; document.form1.onsubmit(); } } 方案二:一次點(diǎn)擊后使得提交按鈕變成不可用 <input type="button" value="login" onclick="this.disabled=true;this.form.submit();" /> 總的來說,js解決方案是基本可以防止重復(fù)點(diǎn)擊提交按鈕造成的重復(fù)提交問題,,但是前進(jìn)后退操作,或者F5刷新頁面等問題并不能得到解決,。 最重要的一點(diǎn),,前端的代碼只能防止不懂js的用戶,如果碰到懂得js的編程人員,,那js方法就沒用了,。 2.設(shè)置HTTP報(bào)頭,控制表單緩存,,使得所控制的表單不緩存信息,,這樣用戶就無法通過重復(fù)點(diǎn)擊按鈕去重復(fù)提交表單。 <meta http-equiv="Cache-Control" content="no-cache, must-revalidate"> 但是這樣做也有局限性,,用戶在提交頁面點(diǎn)擊刷新也會(huì)造成表單的重復(fù)提交,。 3.通過 PRG設(shè)計(jì)模式(用來防止F5刷新重復(fù)提交表單): PRG模式通過響應(yīng)頁面Header返回HTTP狀態(tài)碼進(jìn)行頁面跳轉(zhuǎn)替代響應(yīng)頁面跳轉(zhuǎn)過程。具體過程如下: 客戶端用POST方法請求服務(wù)器端數(shù)據(jù)變更,,服務(wù)器對客戶端發(fā)來的請求進(jìn)行處理重定向到另一個(gè)結(jié)果頁面上,,客戶端所有對頁面的顯示請求都用get方法告知服務(wù)器端,這樣做,,后退再前進(jìn)或刷新的行為都發(fā)出的是get請求,,不會(huì)對server產(chǎn)生任何數(shù)據(jù)更改的影響。 但此方法也不能防止所有情況:例如用戶多次點(diǎn)擊提交按鈕;惡意用戶避開客戶端預(yù)防多次提交手段,,進(jìn)行重復(fù)提交請求; 以上都說的是在客戶端如何防止表單重復(fù)提交,,下面說一下服務(wù)器端有哪些可行的方法,。 4.如果是注冊或存入數(shù)據(jù)庫的操作,可以通過在數(shù)據(jù)庫中字段設(shè)立唯一標(biāo)識(shí)來解決,,這樣在進(jìn)行數(shù)據(jù)庫插入操作時(shí),,因?yàn)槊看尾迦氲臄?shù)據(jù)都相同,數(shù)據(jù)庫會(huì)拒絕寫入,。這樣也避免了向數(shù)據(jù)庫中寫入垃圾數(shù)據(jù)的情況,,同時(shí)也解決了表單重復(fù)提交問題。 但是這種方法在業(yè)務(wù)邏輯上感覺是說不過去的,,本來該有的邏輯,,缺因?yàn)閿?shù)據(jù)庫該有的設(shè)計(jì)隱藏了。而且這種方法也有一定的功能局限性,,只適用于某系特定的插入操作,。 5.session方法: 在struts框架中防止表單重復(fù)提交的方法是生成Token存入session,以此判斷表單是否是第一次提交,。以下給大家解釋一下運(yùn)行流程,。 首先客戶端請求服務(wù)器中的表單,服務(wù)器將客戶機(jī)所請求的表單發(fā)給客戶機(jī)同時(shí)發(fā)送一個(gè)特殊的隨機(jī)數(shù)(Token)作為表單號(hào)存在表單的隱藏域中(type=hidden),,并且存入服務(wù)器端的session中,。在客戶端填寫完表單內(nèi)容向服務(wù)器提交時(shí),同時(shí)也將隱藏域中的表單號(hào)發(fā)給服務(wù)器端,,服務(wù)器端此時(shí)會(huì)檢測服務(wù)器端的表單號(hào)是否存在,如果存在,,則進(jìn)行提交操作,,并刪除此表單號(hào),否則,,服務(wù)器視為客戶機(jī)端重復(fù)提交表單,,不予操作。 此處貼出生成Token的代碼(保證隨機(jī)數(shù)的獨(dú)一無二性): class Token{ private Token(){} private static Token instance = new Token(); public Token getinstance(){ return instance; } //隨機(jī)數(shù)發(fā)生器 public String getToken(){ String token = System.currentTimeMillis() + "" + new Random().nextInt();//獲得毫秒數(shù)加隨機(jī)數(shù) try { MessageDigest md = MessageDigest.getInstance("md5"); byte[] md5 = md.digest(token.getBytes()); BASE64Encoder base = new BASE64Encoder(); base.encode(md5); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } } 要強(qiáng)調(diào)的是,,利用session方法解決表單重復(fù)問題是十分完美的,,基本上可以應(yīng)對各種重復(fù)提交問題。 但,!是不是之前在客戶端防止表單重復(fù)提交的種種方法就不使用了呢,? 答案是否定的,我們需要多種方法混合使用才能達(dá)到最好的效果,,也許有人會(huì)問,,不是說session方法基本可以應(yīng)對各種重復(fù)提交問題了嗎? 這里我們所說的達(dá)到最好效果指的是,,給用戶更好地體驗(yàn),,例如用戶點(diǎn)擊了提交按鈕,,這時(shí)將按鈕變?yōu)椴豢捎玫模靡愿嬖V用戶你已經(jīng)提交內(nèi)容了,,不可重復(fù)提交,。還有如果無論什么情況都用session防止表單重復(fù)提交問題,反而無形的增加了服務(wù)器端的負(fù)擔(dān),。 |
|