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', # 定義了2個自定義的中間件 'middle.md.middle_first', 'middle.md.middle_second' ]
#Auther Bob #--*--conding:utf-8 --*-- from django.utils.deprecation import MiddlewareMixin import time from django.shortcuts import HttpResponse # 中間件就是一個類,下面咱們自定義了2箇中間件,中間件又叫作管道 class middle_first(MiddlewareMixin): def process_request(self,request): print("middle_first--process_request",time.time()) return HttpResponse("大爺慢走") def process_response(self,request,response): print("middle_first--process_response", time.time()) print(response, type(response)) return response class middle_second(MiddlewareMixin): def process_request(self,request): print("middle_second--process_request",time.time()) def process_response(self,request,response): print("middle_second--process_response", time.time()) print(response,type(response)) return response
def process_request(self,request): path = request.path_info # l = path.split("/") # if l[2] in black_list: # return HttpResponse("不能訪問") re_compile_obj = re.compile("/") l = re_compile_obj.split(path) for i in l: if i in black_list: return HttpResponse("不能訪問")
在看process_response方法,參數必需要要有個response,這個參數必需要有,並且這個process_response的函數必需要有返回值,若是沒有,django會把報錯的,最後一箇中間件的process_response把返回值返回給倒數第一個中間件的process_response函數,而後在返回倒數第三個中間件的process_response函數,這個參數默認就是視圖函數的返回,咱們也能夠自定義返回值,下面的例子就是咱們自定義返回值,process_response是按照註冊順序的倒序執行html
def process_response(self,request,response): print("--------->","這個是response的中間件") return HttpResponse("中間件直接返回的")
結果以下前端
咱們還使用默認的response的返回值,也就是用視圖函數的返回值python
def process_response(self,request,response): print("--------->","這個是response的中間件") # return HttpResponse("中間件直接返回的") return response
結果以下django
process_view函數,多箇中間件的process_view函數是按照註冊的正序執行,在執行視圖函數以前執行,若是有返回值,則不會執行視圖函數的方法,執行返回執行process_response函數,若是沒有返回值,則會執行完process_view後在執行視圖函數session
def process_view(self,request,view_func,view_args,view_kwargs): """ :param request: 請求對象 :param view_func: 將要執行的視圖函數 :param view_args:將要執行的函數的位置參數 :param view_kwargs:將要執行的函數的關鍵字參數 :return: """ print("process_view-------->") return HttpResponse("process_view返回值")
上面的例子的process_view函數有返回值,咱們看下結果,能夠看到返回的process_view函數的返回值,沒有去執行視圖函數app
下面這個例子,process_view函數中沒有返回值函數
def process_view(self,request,view_func,view_args,view_kwargs): """ :param request: 請求對象 :param view_func: 將要執行的視圖函數 :param view_args:將要執行的函數的位置參數 :param view_kwargs:將要執行的函數的關鍵字參數 :return: """ print("process_view-------->") # return HttpResponse("process_view返回值")
結果以下,咱們看到返回的是視圖的返回值post
在看下process_exception的中間件的函數學習
def process_exception(self,request,exception): """ :param request: 這個是請求對象 :param exception: 這個是視圖函數返回的錯誤 :return: """ print(exception) print("這個是中間件exception函數") return redirect("https://www.baidu.com")
他的執行順序是按照在中間件註冊的倒序執行的,若是視圖函出錯,纔會執行,下面咱們模擬視圖函數出錯,用raise方法主動拋出錯誤測試
def test_forms(request): print(request.path_info) # for i in range(10): # models.City.objects.create( # name = "北京" + str(i) # ) print(models.City.objects.all().values("name")) if request.method.lower() == "post": myform = Myform(request.POST) # 把post方式傳遞過來的數據傳遞個myform對象 if myform.is_valid(): # 對前端發過來的數據作校驗,若是合法的話 name = myform.cleaned_data.get("name") pwd = myform.cleaned_data.get("pwd") rep_pwd = myform.cleaned_data.get("rep_pwd") print(myform.cleaned_data) else: pass else: myform = Myform() raise ValueError("視圖函數執行失敗了") return render(request,"form1.html",{"myform":myform})
看下打印的信息
視圖函數執行失敗了
這個是中間件exception函數
---------> 這個是response的中間件
先看下中間件在django整個流程處於哪一個階段
中間件的執行順序是這樣的
按照settings裏面MIDDLEWARE的列表中的順序來執行,先執行第一個中間件的process_request方法,而後執行第二個中間件的process_request方法,直到最後一箇中間件的process_request方法,執行完成以後,就到views中的視圖函數,而後返回的時候在從最後一箇中間件的process_response方法,而後執行倒數第二個process_response方法,一直都第一個中間件的process_response方法
這裏咱們須要注意,中間件的函數中的request參數,和視圖函數中的request參數是同樣的,response參數,就是視圖函數中return返回的值
咱們先看下執行順序
首先咱們在middleware中定義的中間件的順序是這樣的
# 定義了2個自定義的中間件 'middle.md.test', 'middle.md.middle_first', 'middle.md.middle_second'
咱們在看先咱們的中間件函數
from django.utils.deprecation import MiddlewareMixin import time from django.shortcuts import HttpResponse # 中間件就是一個類,下面咱們自定義了2箇中間件,中間件又叫作管道 class test(MiddlewareMixin): def process_request(self,request): print("test--process_request", time.time()) def process_response(self,request,response): print("test--process_response", time.time()) return response class middle_first(MiddlewareMixin): def process_request(self,request): print("middle_first--process_request",time.time()) # return HttpResponse("大爺慢走") def process_response(self,request,response): print("middle_first--process_response", time.time()) # print(response, type(response)) return response class middle_second(MiddlewareMixin): def process_request(self,request): print("middle_second--process_request",time.time()) def process_response(self,request,response): print("middle_second--process_response", time.time()) # print(response,type(response)) return response
這裏咱們要注意,在咱們定義的3箇中間件的類中,process_request方法中都沒有return,咱們從客戶端發起請求,看下中間件函數的執行順序,和咱們上面分析的是同樣的
下面咱們在在middle_first類中的process_request方法中定義一個return,在看下執行順序
咱們再次經過客戶端去訪問
咱們在看下執行順序
先執行test的process_request方法,而後執行middle_first的process_request方法,由於這裏的方法中咱們有定義return,因此就不會在往下執行,直接走middle_first的process_response方法,而後執行test的process_response方法
如今咱們對中間件的執行順序應該已經比較清楚了吧
下面咱們經過源代碼來分析一下中間件
首先咱們先看下類中定義的__call__方法,這個方法何時會執行呢?
前面我忘記了,後來測試了一下,如今知道了,咱們在回憶一下
class test_call(object): def __init__(self,call): self.call = call def __call__(self, *args, **kwargs): print("如今是執行{call}".format(call = self.call)) if __name__ == '__main__': t = test_call("cui") t()
執行結果
因此__call__方法是在執行類的對象的時候會調用__call__方法
咱們看到咱們定義的中間件的類都繼承了MiddlewareMixin這個類,咱們來看下這個類是怎麼寫的
class MiddlewareMixin(object): def __init__(self, get_response=None): self.get_response = get_response super(MiddlewareMixin, self).__init__() def __call__(self, request): response = None if hasattr(self, 'process_request'): response = self.process_request(request) if not response: response = self.get_response(request) if hasattr(self, 'process_response'): response = self.process_response(request, response) return response
咱們重點看下看這裏
首先定義response爲空,由於中間件的執行順序是先執行process_request方法,咱們這裏經過反射的hasattr方法,先來判斷是否有定義process_request方法,若是有定義,則執行,執行process_request方法,若是這個方法有返回值,那麼就不會執行第二個if中的代碼,第二個if中的代碼咱們後面在說他的意思,若是,有process_request方法,且有返回值,則會執行第三個if中的方法,就會執行這個中間的process_response方法,這樣就和咱們上面的例子對應上了;
下面咱們來講一下第二個if中的代碼的做用,他的做用就是執行下一個中間件函數的__call__方法,若是沒有下一個中間件,則會執行視圖函數中對應的方法
咱們推薦你們這樣寫,本身實現寫middlewareMixin類,而後咱們本身的中間件類繼承咱們本身寫的middlewareMixin類就能夠了
from django.utils.deprecation import MiddlewareMixin import time from django.shortcuts import HttpResponse class my_MiddlewareMixin(object): def __init__(self, get_response=None): self.get_response = get_response super(my_MiddlewareMixin, self).__init__() def __call__(self, request): response = None if hasattr(self, 'process_request'): response = self.process_request(request) if not response: response = self.get_response(request) if hasattr(self, 'process_response'): response = self.process_response(request, response) return response # 中間件就是一個類,下面咱們自定義了2箇中間件,中間件又叫作管道 class test(my_MiddlewareMixin): def process_request(self,request): print("test--process_request", time.time()) def process_response(self,request,response): print("test--process_response", time.time()) return response class middle_first(my_MiddlewareMixin): def process_request(self,request): print("middle_first--process_request",time.time()) return HttpResponse("大爺慢走") def process_response(self,request,response): print("middle_first--process_response", time.time()) # print(response, type(response)) return response class middle_second(my_MiddlewareMixin): def process_request(self,request): print("middle_second--process_request",time.time()) def process_response(self,request,response): print("middle_second--process_response", time.time()) # print(response,type(response)) return response
class test(my_MiddlewareMixin): def process_request(self,request): print("test--process_request", time.time()) def process_view(self,request,view_func,view_args,view_kwargs): print(view_func, view_args, view_kwargs, sep="-------->") print("test--process_view", time.time()) def process_response(self,request,response): print("test--process_response", time.time()) return response def process_exception(self,request,exception): """ :param request: :param exception: 只能捕獲視圖函數中的異常捕獲,對中間件中的其餘函數的錯誤是捕獲不到的,這裏的excetion就是錯誤的信息 :return: """ print("test--process_exception", time.time())
若是視圖函數中不出錯的話,process_exception是永遠都不會執行的,好比下面一個正常的訪問流程,咱們訪問的test函數沒有出錯,按照順序執行,先執行第一個中間件的process_request方法,而後是第二個中間件的process_request方法,而後是第三個中間件的process_request方法,而後執行第一個中間件的process_view方法,而後是第二個中間件的process_view方法,而後是第三個中間件的process_view方法,而後執行視圖函數,在返回的時候,先執行最後一箇中間件的process_response方法,而後是倒數第二個process_view方法,而後是倒數第三個中間件的processs_response方法
下面咱們看下如何視圖函數中有錯誤的話,會如何執行
咱們首先在視圖函數中構建了一個錯誤
def test(request): int("hahah") print("test------->views",time.time()) return HttpResponse("last_app1")
再次經過客戶端去訪問,咱們在第二個中間件的process_exception方法中作了return返回
執行順序是下面這樣的
先執行第一個中間件的process_request方法,而後是第二個中間件的process_request方法,最後是第三個中間件的process_request方法,而後執行第一個中間件的process_view方法,而後是第二個中間件的process_view方法,最後是執行第三個中間件的process_view方法,這裏很關鍵,由於咱們執行視圖函數報錯,由於咱們人爲構建了一個錯誤,開始執行最後一箇中間件的process_exception方法,執行完成後,執行倒數第二個中間件的process_exception方法,執行到這裏,這個函數有個return返回值,那麼就不會在去執行第一個中間件的process_exception的方法了,這個時候就開始執行最後一箇中間件的process_response方法,而後是倒數第二個中間件的process_exception方法,最後是倒數第三個中間件的process_response方法
補一箇中間件的小例子,就是黑名單的意思,若是訪問的是某個url則直接給返回
下面看下代碼
from django.middleware.security import SecurityMiddleware from django.utils.deprecation import MiddlewareMixin import re from django.shortcuts import HttpResponse from django.shortcuts import render from django.shortcuts import redirect black_list = ["publish","add_publish"] class My_first_mid(MiddlewareMixin): def process_request(self,request): path = request.path_info # l = path.split("/") # if l[2] in black_list: # return HttpResponse("不能訪問") re_compile_obj = re.compile("/") l = re_compile_obj.split(path) for i in l: if i in black_list: return HttpResponse("不能訪問")
前端訪問的效果
首先是能夠訪問
而後是不能訪問
中間件的定義: wsgi以後 urls.py以前 在全局 操做Django請求和響應的模塊! 中間件的使用: 5個固定的方法 process_request(self, request) 執行順序: 按照註冊的順序(在settings.py裏面設置中 從上到下的順序) 什麼時候執行: 請求從wsgi拿到以後 返回值: 返回None,繼續執行後續的中間件的process_request方法 返回response , 不執行後續的中間件的process_request方法 process_response 執行順序: 按照註冊順序的倒序(在settings.py裏面設置中 從下到上的順序) 什麼時候執行: 請求有響應的時候 返回值: 必須返回一個response對象 process_view(self, request, view_func, view_args, view_kwargs): 執行順序: 按照註冊的順序(在settings.py裏面設置中 從上到下的順序) 什麼時候執行: 在urls.py中找到對應關係以後 在執行真正的視圖函數以前 返回值: 返回None,繼續執行後續的中間件的process_view方法 返回response, process_exception(self, request, exception) 執行順序: 按照註冊順序的倒序(在settings.py裏面設置中 從下到上的順序) 什麼時候執行: 視圖函數中拋出異常的時候才執行 返回值: 返回None,繼續執行後續中間件的process_exception 返回response, process_template_response(self, request, response) 執行順序: 按照註冊順序的倒序(在settings.py裏面設置中 從下到上的順序) 什麼時候執行: 視圖函數執行完,在執行視圖函數返回的響應對象的render方法以前 返回值: 返回None,繼續執行後續中間件的process_exception 返回response, Django調用 註冊的中間件裏面五個方法的順序: 1. process_request urls.py 2. process_view view 3. 有異常就執行 process_exception 4. 若是視圖函數返回的響應對象有render方法,就執行process_template_response 5. process_response