轉(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方法請(qǐng)求服務(wù)器端數(shù)據(jù)變更,服務(wù)器對(duì)客戶端發(fā)來的請(qǐng)求進(jìn)行處理重定向到另一個(gè)結(jié)果頁面上,客戶端所有對(duì)頁面的顯示請(qǐng)求都用get方法告知服務(wù)器端,這樣做,后退再前進(jìn)或刷新的行為都發(fā)出的是get請(qǐng)求,不會(huì)對(duì)server產(chǎn)生任何數(shù)據(jù)更改的影響。 但此方法也不能防止所有情況:例如用戶多次點(diǎn)擊提交按鈕;惡意用戶避開客戶端預(yù)防多次提交手段,進(jìn)行重復(fù)提交請(qǐng)求; 以上都說的是在客戶端如何防止表單重復(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)行流程。 首先客戶端請(qǐng)求服務(wù)器中的表單,服務(wù)器將客戶機(jī)所請(qǐng)求的表單發(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)對(duì)各種重復(fù)提交問題。 但!是不是之前在客戶端防止表單重復(fù)提交的種種方法就不使用了呢? 答案是否定的,我們需要多種方法混合使用才能達(dá)到最好的效果,也許有人會(huì)問,不是說session方法基本可以應(yīng)對(duì)各種重復(fù)提交問題了嗎? 這里我們所說的達(dá)到最好效果指的是,給用戶更好地體驗(yàn),例如用戶點(diǎn)擊了提交按鈕,這時(shí)將按鈕變?yōu)椴豢捎玫?,用以告訴用戶你已經(jīng)提交內(nèi)容了,不可重復(fù)提交。還有如果無論什么情況都用session防止表單重復(fù)提交問題,反而無形的增加了服務(wù)器端的負(fù)擔(dān)。 |
|