Django框架開發中避免表單重複提交

  Form表單做爲web2.0時代的重要角色,也是我們與web網站進行數據交互的重要渠道,但是大家在web網站開發過程中,都會遇到一個問題,那就是如何避免表單重複提交,我們可不確定用戶可在提交了一個表單後,是否有足夠的耐心等待我們的程序加載完成,如果此時用戶不耐煩的在前臺重複刷新頁面,那麼就會造成數據重複提交、信息不準確,因此我們在程序設計時一定要規避這樣的問題,接下來介紹一下在Django框架開發中如何避免此問題。

首先說明一下Http協議中兩種非常常見的方法GET和POST。

1、GET方法往往被視爲向服務器索取信息的一種手段,但是通過使用"?"和"&"符號也可以將不同的表單項提交給服務器,例如"/test?id=1"中我們就將id=1提交給了後臺,有時在一些特殊情況中,我們甚至不需要在html中寫form表單,只要我們保證url正確就可以了,但是使用GET方式提交也有一個問題需要注意,IE對於URL長度的限制是2083個字節,其他瀏覽器或是web server都有不同的限制,所以在提交較長的內容時,GET方法就有些不合適了。

2、POST方法相比GET方法在數據提交上更安全一些,因爲GET方法將參數統一放到了URL中,隨後在HTTP包頭中發向服務器,但是提交的參數也就無疑被暴露了,在瀏覽器中可以直觀的看到提交的具體內容,在web server中也可以通過訪問日誌隨便查看提交的內容,對於有安全性的數據一定是採用POST方式來提交的,同時對於密碼等敏感內容也一定要做加密處理,採用POST方法對於數據內容理論上也沒有長度限制,這樣就可以提交更多的數據內容了。

  使用POST方法提交表單往往可以增加信息傳輸的安全性,如果表單項過多也避免了較長的url,因此在web開發中我更喜歡用POST方法,回到之前的問題,就是頁面提交過後,此時如果刷新頁面,那麼瀏覽器會提示重新提交表單,此時如果我們點擊繼續後,那麼瀏覽器就會將之前提交過的內容再次發送至服務器,如果我們在後臺沒有做限制處理,那麼數據就被重複提交了,試想一下如果A用戶要轉賬給B用戶,A用戶輸入轉賬金額2000元,在表單提交後,後臺根據A用戶發送的請求在數據庫裏更新A賬戶信息,如果此時A用戶重複刷新頁面,那麼後臺豈不是要連續的從A用戶里扣除2000元增加到B用戶中麼,雖然現實生活中不可能發生這樣的事情,但是這樣卻很好的說明了表單重複提交的危險性,因此對於POST方法提交表單,我們可以用下面的方式來避免重複提交。

1、URL重定向

  這種方式較爲簡單,我們可以在用戶提交的表單後,在提交頁面做一個讀秒的倒計時提示,這也是我們在大多數網站上經常見到的,在倒計時結束後,頁面被重新定向到上一級頁面,或是我們希望被跳轉的頁面,這樣在一定程度上就避免了重複刷新帶來的風險,或是在頁面提交後直接進行URL重定向,在Django的視圖中我們可以使用HttpResponseRedirect來重定向頁面。

2、Cookie驗證

  上面的方式我們通過頁面來進行控制,也就是說提交過後頁面url放生了變化,此時用戶再次刷新時就不會請求之前提交的頁面,除了URL重定向,我們也可以通過cookie中key-value來做進一步限制,首先進入提交頁前我們在cookie中寫入一個值,在提交過後重置該值,這樣後臺通過獲取cookie中的值就可以做判斷了,此時如果用戶再次刷新頁面時,就可以返回提示說明信息已經提交過,不需要重複提交,在django框架中的代碼如下。

# 表單視圖
def page(request):
    response = render_to_response('post.html')
    # 在客戶端Cookie中添加Post表單token,避免用戶重複提交表單
    response.set_cookie("postToken",value='allow')
    return response
 
#提交視圖
def post(request):
    # 檢測Token值,判斷用戶提交動作是否合法
    if request.COOKIES["postToken"] !='allow':
        # 不存在合法Token,該表單爲重複提交
        return HttpResponse("you can't post it again.")
    '''
    代碼邏輯處理段
    '''
    response = render_to_response('newPage.html')
    # 表單POST提交成功,重置客戶端Cookie中存在的Token值,避免重複提交
    response.set_cookie("postToken",value='disable')
    return response

3、Hidden屬性的隱藏域

  上一種方式我們在用戶的瀏覽器中寫入了指定的token值,我們也可以在服務器的session會話中寫入特定的信息,然後將內容渲染到html頁面中表單的隱藏域,用戶從頁面中是看不到這個隱藏域的,一般有特殊信息提交時會用到,比如這裏的重複提交驗證,我們就可以用表單的Hidden這個屬性,我們將服務器生成的值寫入到表單,隨用戶操作一起提交回服務器,那麼如果該值與服務器端匹配,程序繼續執行,否則認爲重複提交,這裏要注意與cookie操作中一樣,我們在提交完後,服務器一定要清空session中的值。

# 表單視圖
def page(request):
    # 在服務端session中添加key認證,避免用戶重複提交表單
    token = allow # 可以採用隨機數
    request.session['postToken'] = token
    # 將Token值返回給前臺模板
    return render_to_response('post.html','postToken':token)
 
#提交視圖
def post(request):
    # 檢測session中Token值,判斷用戶提交動作是否合法
    Token = request.session.get(postToken,default=None)
    # 獲取用戶表單提交的Token值
    userToken = request.POST['postToken']
    if  userToken !=Token:
        # 不存在合法Token,該表單爲重複提交
        return HttpResponse("you can't post it again.")
    '''
    代碼邏輯處理段
    '''
    # 表單POST提交成功,重置服務端中存在的Token值,避免重複提交
    del request.session['postToken']
    return render_to_response('newPage.html')

4、驗證碼

  我們可以在表單頁中加入驗證碼,每次提交時服務端來做驗證,但是這樣的方式又影響了提交操作的便捷度,有得有失。

對於GET的提交方式較爲簡單,在提交後重新定義url地址就可以了,因爲url中沒有了參數,也就避免了重複提交,但是爲了避免用戶重新訪問之前的url地址,我們也可以結合上面的方法來對GET方法進行限制。

寫在最後

  對於表單重複提交的情況,我們發現往往最容易出現在表單頁與提交頁URL相同、或是頁面提交後依然停留在提交頁URL中,在Django框架中,我認爲表單頁與提交頁應該儘量使用不同的URL,不同的視圖,這樣我們可以更好的避免該問題,例如在提交後我們將URL重定向到表單頁,甚至可以在前臺使用Ajax異步來提交數據,這樣提交動作不會影響到頁面變化,用戶再次刷新也只能刷新當前的表單頁了。相信在web程序開發中一定還有更多種方法來規避表單重複提交的問題,這裏所展示的也不一定是最完整準確的,但在web程序開發中我們一定要考慮到類似這樣的異常,這纔是最重要的。

原創文章首發自阿布的博客,本文地址:http://www.abuve.com/article/17/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章