django的中間件:process_request|process_response|process_view|process_exception





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

  

中間件函數的參數是固定的

先看process_request方法,參數有一個request

  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

相關文章
相關標籤/搜索