中間件顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,而且在全局上改變django的輸入與輸出。由於改變的是全局,因此須要謹慎實用,用很差會影響到性能。html
Django的中間件的定義:python
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對象,這些均可以經過中間件來實現。django
可能還想在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配置項是一個列表,列表中是一個個字符串,這些字符串實際上是一個個類,也就是一個個中間件。session
咱們以前已經接觸過一個csrf相關的中間件了,以前將這一行註釋,再提交post請求的時候,就不會被forbidden了,後來學會使用csrf_token就不用再註釋這個中間件了。app
手動引入一箇中間件:dom
from django.middleware.security import SecurityMiddleware
查看中間件源碼:xss
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
每一箇中間件都有具體的功能。ide
中間件能夠定義五個方法,分別是:(主要的是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對象,則直接將該對象返回給用戶。
當用戶發起請求的時候會依次通過全部的的中間件,這個時候的請求是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
流程圖以下:
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直接返回,則能夠跳過視圖函數這一步直接返回。
僅修改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
僅修改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") 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都還會執行。
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出現錯誤時流程圖以下所示:
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
僅對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即返回給瀏覽器了。
某些IP訪問服務器的頻率太高,進行攔截,好比限制每分鐘不能超過20次。
若是用戶訪問的是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',