middleware 中間件就是在目標和結果之間進行的額外處理過程,在Django中就是request和response之間進行的處理,相對來說實現起來比較簡單,但是要註意它是對全局有效的,可以在全局範圍內改變輸入和輸出結果,因此需要謹慎使用,否則不僅會造成難以定位的錯誤,而且可能會影響整體性能。 ...
middleware
中間件就是在目標和結果之間進行的額外處理過程,在Django中就是request和response之間進行的處理,相對來說實現起來比較簡單,但是要註意它是對全局有效的,可以在全局範圍內改變輸入和輸出結果,因此需要謹慎使用,否則不僅會造成難以定位的錯誤,而且可能會影響整體性能。
中間件有什麼用
如果想要修改HttpRequest或者HttpResponse,就可以通過中間件來實現。
- 登陸認證:在中間件中加入登陸認證,所有請求就自動擁有登陸認證,如果需要放開部分路由,只需要特殊處理就可以了。
- 流量統計:可以針對一些渲染頁面統計訪問流量。
- 惡意請求攔截:統計IP請求次數,可以進行頻次限制或者封禁IP。
本身django項目就帶有很多的中間件,在setting.py
里
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
之前我們通過post請求發起的時候,就需要先註釋掉csrf那一個中間件。
每個中間件的功能如下,
SecurityMiddleware
:為request/response提供了幾種安全改進;SessionMiddleware
:開啟session會話支持;CommonMiddleware
:基於APPEND_SLASH和PREPEND_WWW的設置來重寫URL,如果APPEND_SLASH設為True,並且初始URL 沒有以斜線結尾以及在URLconf 中沒找到對應定義,這時形成一個斜線結尾的新URL;CsrfViewMiddleware
:添加跨站點請求偽造的保護,通過向POST表單添加一個隱藏的表單欄位,並檢查請求中是否有正確的值;AuthenticationMiddleware
:在視圖函數執行前向每個接收到的user對象添加HttpRequest屬性,表示當前登錄的用戶,無它用不了request.user
。MessageMiddleware
:開啟基於Cookie和會話的消息支持XFrameOptionsMiddleware
:對點擊劫持的保護
除此以外, Django還提供了壓縮網站內容的GZipMiddleware
,根據用戶請求語言返回不同內容的LocaleMiddleware
和給GET請求附加條件的ConditionalGetMiddleware
。這些中間件都是可選的。
Django的中間件執行順序
當你在settings.py
註冊中間件時一定要要考慮中間件的執行順序,中間件在request到達view之前是從上向下執行的,在view執行完後返回response過程中是從下向上執行的,如下圖所示。舉個例子,如果你自定義的中間件有依賴於request.user
,那麼你自定義的中間件一定要放在AuthenticationMiddleware
的後面
工作原理
HTTP Web伺服器工作原理一般都是接收用戶發來的請求(request), 然後給出響應(response)。Django也不例外,其一般工作方式是接收request對象和其它參數,交由視圖(view)處理,然後給出它的響應(respone)數據: 渲染過的html文件或json格式的數據。然而在實際工作中Django並不是接收到request對象後,馬上交給視圖函數或類(view)處理,也不是在view執行後立馬給用戶返回reponse。事實上Django最初接收的是HttpRequest對象,而不是request對象,正是中間件的作用把HttpRequest對象和user對象打包成了一個全局變數request對象,這樣你才可以View中使用request作為變數或者在模板中隨意調用request.user。
中間件(Middleware)在整個Django的request/response處理機制中的角色如下所示:
HttpRequest -> Middleware -> View -> Middleware -> HttpResponse
正是由於一個請求HttpRequest在傳遞給視圖View處理前要經過中間件處理,經過View處理後的響應也要經過中間件處理才能返回給用戶,我們可以編寫自己的中間件實現許可權校驗,限制用戶請求、列印日誌、改變輸出內容等多種應用場景,比如:
- 禁止特定IP地址的用戶或未登錄的用戶訪問我們的View視圖函數
- 對同一IP地址單位時間內發送的請求數量做出限制
- 在View視圖函數執行前記錄用戶的IP地址
- 在View視圖函數執行前傳遞額外的變數或參數
- 在View視圖函數執行前或執行後把特定信息列印到log日誌
- 在View視圖函數執行後對reponse數據進行修改後返回給用戶
值得一提的是中間件對Django的輸入或輸出的改變是全局的,反之亦然。如果讓你希望對Django的輸入或輸出做出全局性的改變時,需要使用中間件。舉個例子,我們在裝飾器一文中介紹瞭如何使用@login_required裝飾器要求用戶必須先登錄才能訪問我們的視圖函數。試想我們有個網站絕大部分視圖函數都需要用戶登錄,每個視圖函數前面都需要加上@login_required裝飾器是比較傻的行為。藉助於中間件,我們無需使用裝飾器即可全局實現:只有登錄用戶才能訪問視圖函數,匿名用戶跳轉到登錄頁面。實現原理也很簡單,在一個request到達視圖函數前,我們先對request.user是否驗證通過進行判斷,然後再進行跳轉。另外Django對POST表單中攜帶的CSRF token的全局校驗也是通過CsrfViewMiddleware這個中間件進行的,而不是通過單個裝飾器實現的。
自定義中間件
一般的中間件必須要有的兩個函數就是process_request
和process_response
如果自己定義中間件,首先在app里創建一個py文件,然後導入一些自帶的模塊
from django.utils.deprecation import MiddlewareMixin
然後就是自定義類
class ReginaMiddleWare(MiddlewareMixin):
def process_request(self,request):
'''
:param request: 請求信息對象
:return:
'''
print("reginaMiddleWare",request)
def process_response(self,response,request):
'''
:param response: 請求信息對象
:param request: 視圖函數返回的響應體
:return:
'''
print(response)
return response
第三步就是把自己寫好的中間件按照目錄位置添加到settings文件當中
MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'reginauser.reginaMiddleware.ReginaMiddleWare',
'reginauser.reginaMiddleware.IvanleeMiddleWare'
]
reginaMiddleWare <WSGIRequest: GET '/md/'>
ivanleeMiddleWare <WSGIRequest: GET '/md/'>
<WSGIRequest: GET '/md/'>
ivanleemiddleware <WSGIRequest: GET '/md/'>
reginamiddleware <WSGIRequest: GET '/md/'>
process_request功能
ip = request.META.get("REMOTE_ADDR")
if ip in ['127.0.0.1','']:
return HttpResponse("非法ip")
我們可以試一下本地地址,一旦process_request返回的不是預設的None,則表示攔截成功,那麼也不會經過IvanleeMiddleware,直接回到ReginaMiddleware的響應處原路返回
process_response
def process_response(self,response,request):
'''
:param response: 請求信息對象
:param request: 視圖函數返回的響應體
:return:
'''
print("ivanleeMiddleWare_response")
respnse = response.content + "<h1>I love Regina</h1>"
return respnse
那麼在返回的時候,響應內容就是原有的helloworld和IloveRegina一起發送給請求者。
本文來自博客園,作者:ivanlee717,轉載請註明原文鏈接:https://www.cnblogs.com/ivanlee717/p/16853099.html