天天看點

java web解決表單重複送出問題

首先我們在讨論如何解決表單重複送出問題之前先來解決三個問題:1.什麼叫表單重複送出?2.什麼情況下會出現表單重複送出?3.什麼情況需要避免表單重複送出?

什麼叫表單送出問題,說白了,就是同一份資訊,重複的送出給伺服器。

那麼,在什麼情況下回産生表單重複送出的情況呢?

給大家列舉以下情況:

1.點選F5重新整理頁面:當使用者點選submit将已經寫好的表單資料送出到伺服器時,可以在浏覽器的url看到位址和參數的變化,但因為網速等問題,使用者目前頁面并未重新整理,或者點選重新整理頁面,造成表單重複送出。

2.重複點選送出按鈕:因為網絡問題,未能及時跳轉顯示内同,部分使用者可能會出于心急重複送出送出按鈕,造成多次送出内容到伺服器。

3.前進後退操作:有些使用者在進行某些工作操作時,可能出于需要或者某種情況,進行後退操作,浏覽剛才填入的資訊,在進行後退和前進的操作可能也會造成表單資料重複送出到伺服器。

4.使用浏覽器曆史記錄重複通路:某些使用者可能會出于好奇,利用浏覽器的曆史記錄功能重複通路送出頁面,同樣會造成表單重複送出問題。

其實我們在進行web開發的時候,遇到的所有問題不一定是都需要解決的,大多數問題需要分情況而定。

例如現在所說的表單重複送出問題

那什麼情況下必須防止表單重複送出問題呢:例如說注冊使用者功能(向資料庫中寫入垃圾資訊),投票功能(多次刷票),購物結算功能(多次扣錢),防止論壇灌水功能。

明确了以上三個問題後,我們來讨論如何解決表單重複送出問題:

翻閱了衆多部落格發現表單重複問題可以有多種解決方案,總結如下:

1.js方法解決:關于js方法解決就是說通過js動态控制送出按鈕不能多次點選,或者多次點選不起作用。

方案一:通過設立辨別使表單不能重複送出:

var flag=true;

    function Sub(){

        if(flag){

            flag = false;

            document.form1.onsubmit();

        }

    }

方案二:一次點選後使得送出按鈕變成不可用

<input type="button" value="login" οnclick="this.disabled=true;this.form.submit();" />

總的來說,js解決方案是基本可以防止重複點選送出按鈕造成的重複送出問題,但是前進後退操作,或者F5重新整理頁面等問題并不能得到解決。

最重要的一點,前端的代碼隻能防止不懂js的使用者,如果碰到懂得js的程式設計人員,那js方法就沒用了。

2.設定HTTP報頭,控制表單緩存,使得所控制的表單不緩存資訊,這樣使用者就無法通過重複點選按鈕去重複送出表單。

<meta http-equiv="Cache-Control" content="no-cache, must-revalidate">

但是這樣做也有局限性,使用者在送出頁面點選重新整理也會造成表單的重複送出。

3.通過PRG設計模式(用來防止F5重新整理重複送出表單):

PRG模式通過響應頁面Header傳回HTTP狀态碼進行頁面跳轉替代響應頁面跳轉過程。具體過程如下:

用戶端用POST方法請求伺服器端資料變更,伺服器對用戶端發來的請求進行處理重定向到另一個結果頁面上,用戶端所有對頁面的顯示請求都用get方法告知伺服器端,這樣做,後退再前進或重新整理的行為都發出的是get請求,不會對server産生任何資料更改的影響。

但此方法也不能防止所有情況:例如使用者多次點選送出按鈕;惡意使用者避開用戶端預防多次送出手段,進行重複送出請求;

以上都說的是在用戶端如何防止表單重複送出,下面說一下伺服器端有哪些可行的方法。

4.如果是注冊或存入資料庫的操作,可以通過在資料庫中字段設立唯一辨別來解決,這樣在進行資料庫插入操作時,因為每次插入的資料都相同,資料庫會拒絕寫入。這樣也避免了向資料庫中寫入垃圾資料的情況,同時也解決了表單重複送出問題。

但是這種方法在業務邏輯上感覺是說不過去的,本來該有的邏輯,缺因為資料庫該有的設計隐藏了。而且這種方法也有一定的功能局限性,隻适用于某系特定的插入操作。

5.session方法:

在struts架構中防止表單重複送出的方法是生成Token存入session,以此判斷表單是否是第一次送出。以下給大家解釋一下運作流程。

首先用戶端請求伺服器中的表單,伺服器将客戶機所請求的表單發給客戶機同時發送一個特殊的随機數(Token)作為表單号存在表單的隐藏域中(type=hidden),并且存入伺服器端的session中。

在用戶端填寫完表單内容向伺服器送出時,同時也将隐藏域中的表單号發給伺服器端,伺服器端此時會檢測伺服器端的表單号是否存在,如果存在,則進行送出操作,并删除此表單号,否則,伺服器視為客戶機端重複送出表單,不予操作。

此處貼出生成Token的代碼(保證随機數的獨一無二性):

class Token{

    private Token(){}

    private static Token instance = new Token();

    public Token getinstance(){

        return instance;

    }

    //随機數發生器

    public String getToken(){

        String token = System.currentTimeMillis() + "" + new Random().nextInt();//獲得毫秒數加随機數

        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;

    }

}

要強調的是,利用session方法解決表單重複問題是十分完美的,基本上可以應對各種重複送出問題。

但!是不是之前在用戶端防止表單重複送出的種種方法就不使用了呢?

答案是否定的,我們需要多種方法混合使用才能達到最好的效果,也許有人會問,不是說session方法基本可以應對各種重複送出問題了嗎?

這裡我們所說的達到最好效果指的是,給使用者更好地體驗,例如使用者點選了送出按鈕,這時将按鈕變為不可用的,用以告訴使用者你已經送出内容了,不可重複送出。還有如果無論什麼情況都用session防止表單重複送出問題,反而無形的增加了伺服器端的負擔。