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/