一、中間件的概念
中間件顧名思義,是介于request與response處理之間的一道處理過程,相對比較輕量級,并且在全局上改變django的輸入與輸出。因為改變的是全局,是以需要謹慎實用,用不好會影響到性能。
Django的中間件的定義:
Middleware is a framework of hooks into Django’s request/response processing. <br>It’s a light, low-level “plugin” system for globally altering Django’s input or output.
如果想修改請求,例如被傳送到view中的HttpRequest對象。 或者想修改view傳回的HttpResponse對象,這些都可以通過中間件來實作。
可能還想在view執行之前做一些操作,這種情況就可以用 middleware來實作。
Django預設的
Middleware可以在settings.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',
]
MIDDLEWARE配置項是一個清單,清單中是一個個字元串,這些字元串其實是一個個類,也就是一個個中間件。
我們之前已經接觸過一個csrf相關的中間件了,之前将這一行注釋,再送出post請求的時候,就不會被forbidden了,後來學會使用csrf_token就不用再注釋這個中間件了。
手動引入一個中間件:
from django.middleware.security import SecurityMiddleware
檢視中間件源碼:

import re
from django.conf import settings
from django.http import HttpResponsePermanentRedirect
from django.utils.deprecation import MiddlewareMixin
class SecurityMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
self.sts_seconds = settings.SECURE_HSTS_SECONDS
self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS
self.sts_preload = settings.SECURE_HSTS_PRELOAD
self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF
self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER
self.redirect = settings.SECURE_SSL_REDIRECT
self.redirect_host = settings.SECURE_SSL_HOST
self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT]
self.get_response = get_response
def process_request(self, request):
path = request.path.lstrip("/")
if (self.redirect and not request.is_secure() and
not any(pattern.search(path)
for pattern in self.redirect_exempt)):
host = self.redirect_host or request.get_host()
return HttpResponsePermanentRedirect(
"https://%s%s" % (host, request.get_full_path())
)
def process_response(self, request, response):
if (self.sts_seconds and request.is_secure() and
'strict-transport-security' not in response):
sts_header = "max-age=%s" % self.sts_seconds
if self.sts_include_subdomains:
sts_header = sts_header + "; includeSubDomains"
if self.sts_preload:
sts_header = sts_header + "; preload"
response["strict-transport-security"] = sts_header
if self.content_type_nosniff and 'x-content-type-options' not in response:
response["x-content-type-options"] = "nosniff"
if self.xss_filter and 'x-xss-protection' not in response:
response["x-xss-protection"] = "1; mode=block"
return response
SecurityMiddleware源碼
每個中間件都有具體的功能。
二、自定義中間件
中間件可以定義五個方法,分别是:(主要的是process_request和process_response)
process_request(self,request)
process_view(self, request, view_func, view_args, view_kwargs)
process_template_response(self,request,response)
process_exception(self, request, exception)
process_response(self, request, response)
以上方法的傳回值可以是None或一個HttpResponse對象,如果是None,則繼續按照django定義的規則向後繼續執行,如果是HttpResponse對象,則直接将該對象傳回給使用者。
1、process_request, process_response
當使用者發起請求的時候會依次經過所有的的中間件,這個時候的請求是process_request,最後到達views的函數中,views函數處理後,在依次穿過中間件,這個時候是process_response,最後傳回給請求者。
上述截圖中的中間件都是django中的,我們也可以自己定義一個中間件,自己寫一個類,但是必須繼承MiddlewareMixin。
自定義中間件需要引入:
from django.utils.deprecation import MiddlewareMixin
在視圖函數中:
def index(request):
print("index.....")
return HttpResponse("Index")
在自定義中間件my_middlewares.py中:
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class CustomerMiddleware(MiddlewareMixin):
def process_request(self, request):
print("CustomMiddleware process_request...")
def process_response(self, request, response):
# 必須有傳回值,要一層層往回傳,不加就會報錯
print("CustomMiddleware process_response")
return response
class CustomerMiddleware2(MiddlewareMixin):
def process_request(self, request):
print("CustomMiddleware2 process_request...")
def process_response(self, request, response):
print("CustomMiddleware2 process_response")
return response
通路index頁面,控制台輸出結果:
CustomMiddleware process_request...
CustomMiddleware2 process_request...
index.....
CustomMiddleware2 process_response
CustomMiddleware process_response
注意:1 .在process_request函數中,如果添加了return語句,會發生中斷現象,程式把請求發給中間件,然後依次傳回給請求者。
2.在process_response函數中,一定要有return語句,因為需要一層層往回傳值給請求者(浏覽器)。
(1)如果是在CustomerMiddleware類中的process_request函數中添加return HttpResponse("forbidden..."),輸出如下:
網頁顯示:
forbidden...
控制台輸出:
CustomMiddleware process_request...
CustomMiddleware process_response
(2)如果是在CustomerMiddleware2類中的process_request函數中添加return HttpResponse("forbidden..."),輸出如下:
網頁顯示:
forbidden...
控制台輸出
CustomMiddleware process_request...
CustomMiddleware2 process_request...
CustomMiddleware2 process_response
CustomMiddleware process_response
流程圖如下:
2、process_view
def process_view(self, request, callback, callback_args, callback_kwargs):...
my_middlewares.py修改如下:
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class CustomerMiddleware(MiddlewareMixin):
def process_request(self, request):
print("CustomMiddleware process_request...")
def process_response(self, request, response):
# 必須有傳回值,要一層層往回傳,不加就會報錯
print("CustomMiddleware process_response")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("CustomMiddleware1 process_view")
class CustomerMiddleware2(MiddlewareMixin):
def process_request(self, request):
print("CustomMiddleware2 process_request...")
def process_response(self, request, response):
print("CustomMiddleware2 process_response")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("CustomMiddleware2 process_view")
執行結果如下所示:
網頁顯示:
index
控制台輸出
CustomMiddleware process_request...
CustomMiddleware2 process_request...
CustomMiddleware1 process_view
CustomMiddleware2 process_view
index.....
CustomMiddleware2 process_response
CustomMiddleware process_response
上述代碼運作過程見下圖:
當最後一個中間件的process_request到達路由關系映射之後,傳回到中間件1的process_view,然後依次往下,到達views函數,最後通過process_response依次傳回到達使用者。
意義:在中間件走完時,多走了一步路由控制,而callback參數就是用來找到這次請求對應的視圖函數(callback_args是視圖函數的參數),是以可以提前一步來執行視圖函數,而如果return直接傳回,則可以跳過視圖函數這一步直接傳回。
示例1:用process_view來調用視圖函數
僅修改CustomMiddleware2的process_view函數,修改如下:
class CustomerMiddleware2(MiddlewareMixin):
def process_request(self, request):
print("CustomMiddleware2 process_request...")
# return HttpResponse("forbidden...")
def process_response(self, request, response):
print("CustomMiddleware2 process_response")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("====》", callback(callback_args))
print("CustomMiddleware2 process_view")
輸出如下所示:
CustomMiddleware process_request...
CustomMiddleware2 process_request...
CustomMiddleware1 process_view
index.....
====》 <HttpResponse status_code=200, "text/html; charset=utf-8">
CustomMiddleware2 process_view
index.....
CustomMiddleware2 process_response
CustomMiddleware process_response
示例2:給process_view函數傳回值
class CustomerMiddleware2(MiddlewareMixin):
def process_request(self, request):
print("CustomMiddleware2 process_request...")
# return HttpResponse("forbidden...")
def process_response(self, request, response):
print("CustomMiddleware2 process_response")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
# print("====》", callback(callback_args))
print("CustomMiddleware2 process_view")
return HttpResponse("123")
輸出如下所示:
頁面顯示:
123
控制台輸出:
CustomMiddleware process_request...
CustomMiddleware2 process_request...
CustomMiddleware1 process_view
CustomMiddleware2 process_view
CustomMiddleware2 process_response
CustomMiddleware process_response
注意:process_view如果有傳回值,會越過其他的process_view以及視圖函數,但是所有的process_response都還會執行。
3、process_exception
def process_exception(self, request, exception):...
示例修改如下:
class CustomerMiddleware(MiddlewareMixin):
def process_request(self, request):
print("CustomMiddleware1 process_request...")
def process_response(self, request, response):
# 必須有傳回值,要一層層往回傳,不加就會報錯
print("CustomMiddleware1 process_response")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("CustomMiddleware1 process_view")
def process_exception(self, request, exception):
print("CustomMiddleware1 process_exception")
class CustomerMiddleware2(MiddlewareMixin):
def process_request(self, request):
print("CustomMiddleware2 process_request...")
def process_response(self, request, response):
print("CustomMiddleware2 process_response")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("CustomMiddleware2 process_view")
def process_exception(self, request, exception):
print("CustomMiddleware2 process_exception")
網頁顯示:
Index
控制台輸出:
CustomMiddleware1 process_request...
CustomMiddleware2 process_request...
CustomMiddleware1 process_view
CustomMiddleware2 process_view
index.....
CustomMiddleware2 process_response
CustomMiddleware1 process_response
注意:在代碼正常情況下不會執行,當視圖中報錯時,才會依次執行process_excepsion和process_response。
當views出現錯誤時流程圖如下所示:
(1)在視圖函數中填寫錯誤代碼
def index(request):
print("index.....")
yuan
return HttpResponse("Index")
輸出如下:
1)頁面顯示錯誤提示
2)控制台輸出
CustomMiddleware1 process_request...
CustomMiddleware2 process_request...
CustomMiddleware1 process_view
CustomMiddleware2 process_view
Internal Server Error: /index/
index.....
CustomMiddleware2 process_exception
CustomMiddleware1 process_exception
大段的錯誤提示
CustomMiddleware2 process_response
CustomMiddleware1 process_response
(2)在process_exception中抓取異常資訊,傳回到頁面中顯示
僅對CustomerMiddleware2中的process_exception修改:
class CustomerMiddleware2(MiddlewareMixin):
def process_request(self, request):
print("CustomMiddleware2 process_request...")
# return HttpResponse("forbidden...")
def process_response(self, request, response):
print("CustomMiddleware2 process_response")
return response
def process_view(self, request, callback, callback_args, callback_kwargs):
print("CustomMiddleware2 process_view")
def process_exception(self, request, exception):
print("CustomMiddleware2 process_exception")
return HttpResponse(exception)
頁面顯示:
name 'yuan' is not defined
控制台輸出:
CustomMiddleware1 process_request...
CustomMiddleware2 process_request...
CustomMiddleware1 process_view
CustomMiddleware2 process_view
index.....
CustomMiddleware2 process_exception
CustomMiddleware2 process_response
CustomMiddleware1 process_response
CustomMiddleware2的process_exception在捕獲到錯誤後,把return值作為響應體直接傳回了,就不再執行後面的exception了(CustomMiddleware1的),再依次傳給process_response即傳回給浏覽器了。
三、中間件的應用
1、做IP通路頻率限制
某些IP通路伺服器的頻率過高,進行攔截,比如限制每分鐘不能超過20次。
2、URL通路過濾
如果使用者通路的是login視圖(放過)
如果通路其他視圖,需要檢測是不是有session認證,已經有了放行,沒有傳回login,這樣就省得在多個視圖函數上寫裝飾器了!
(1)在settings.py中定義白名單:
# 白名單
WHITE_LIST = ["/login/", "/reg/", "/logout/"]
(2)建立自定義的中間件檔案./app01/my_middlewares.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect
from authDemo import settings
class AutoMiddleware(MiddlewareMixin):
def process_request(self, request):
# 拿到白名單
white_list = settings.WHITE_LIST
if request.path in white_list:
# 路徑在白名單中直接通過
return None # return None等同于不寫return ,中間件通過
# 不在白名單中的路徑需要校驗是否登入驗證
if not request.user.is_authenticated:
# 未經過校驗跳轉到登入頁面
return redirect("/login/")
(3)在settings中添加自定義中間件
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',
"app01.my_middlewares.AutoMiddleware",
]
運作程式,并通路index頁面,發現跳轉到登入頁面。
雖然中間件很友善,但不是所有情況下都應該用中間件,稍有不慎就會降低程式效率。往往視圖函數大多數都需要校驗,則使用中間件比較合适,隻有少量需要校驗則還是使用@login_required裝飾器更加合适。
四、中間件源碼試讀
主要嘗試着讀以下兩個自帶的中間件:
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',