Django組件-中間件


1、中間件的概念

  中間件顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,並且在全局上改變django的輸入與輸出。因為改變的是全局,所以需要謹慎實用,用不好會影響到性能。

  Django的中間件的定義:

Middleware is a framework of hooks into Django’s request/response processing. 
It’s a light, low-level “plugin” system for globally altering Django’s input or output.

  如果你想修改請求,例如被傳送到view中的HttpRequest對象。 或者你想修改view返回的HttpResponse對象,這些都可以通過中間件來實現。

  可能你還想在view執行之前做一些操作,這種情況就可以用 middleware來實現。

  Django默認的Middleware:

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之后就不再注釋這個中間件了。

  那接下來就為大家解開神秘的面紗,深入探索一下。學習中間件中的方法以及這些方法什么時候被執行。

2、自定制中間件

  中間件可以定義五個方法,分別是:

    • process_request(self,request)
    • process_view(self, request, view_func, view_args, view_kwargs)
    • process_exception(self, request, exception)
    • process_response(self, request, response)
    • process_template_response(self,request,response)(了解)

3、自定制中間件示例

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")

    def process_response(self, request, response):
        print("MD1里面的 process_response")
        return response

4、process_request方法

  process_request有一個參數,就是request,這個request和視圖函數中的request是一樣的。

  它的返回值可以是None也可以是HttpResponse對象。返回值是None的話,按正常流程繼續走,交給下一個中間件處理,如果是HttpResponse對象,Django將不執行視圖函數,而將響應對象返回給瀏覽器。

  我們來看看多個中間件時,Django是如何執行其中的process_request方法的。

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")

  在settings.py的MIDDLEWARE配置項中注冊上述兩個自定義中間件:

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',
    'middlewares.MD1',  # 自定義中間件MD1
    'middlewares.MD2'  # 自定義中間件MD2
]

  此時,我們訪問一個視圖,會發現終端中打印如下內容:

MD1里面的 process_request
MD2里面的 process_request
app01 中的 index視圖

  把MD1和MD2的位置調換一下,再訪問一個視圖,會發現終端中打印的內容如下:

MD2里面的 process_request
MD1里面的 process_request
app01 中的 index視圖

  看結果我們知道:視圖函數還是最后執行的,MD2比MD1先執行自己的process_request方法。

  在打印一下兩個自定義中間件中process_request方法中的request參數,會發現它們是同一個對象。

  由此總結一下:

    1. 中間件的process_request方法是在執行視圖函數之前執行的。

    2. 當配置多個中間件時,會按照MIDDLEWARE中的注冊順序,也就是列表的索引值,從前到后依次執行的。

    3. 不同中間件之間傳遞的request都是同一個對象

5、process_response方法

  它有兩個參數,一個是request,一個是response,request就是上述例子中一樣的對象,response是視圖函數返回的HttpResponse對象。該方法的返回值也必須是HttpResponse對象。

  給上述的M1和M2加上process_response方法:

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")

    def process_response(self, request, response):
        print("MD1里面的 process_response")
        return response


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")

    def process_response(self, request, response):
        print("MD2里面的 process_response")
        return response

  訪問一個視圖,看一下終端的輸出:

MD1里面的 process_request
MD2里面的 process_request
app01 中的 index視圖
MD2里面的 process_response
MD1里面的 process_response

  看結果可知:

    process_response方法是在視圖函數之后執行的,並且順序是MD1比MD2先執行。(此時settings.py中 MD2比MD1先注冊)

    多個中間件中的process_response方法是按照MIDDLEWARE中的注冊順序倒序執行的,也就是說第一個中間件的process_request方法首先執行,而它的process_response方法最后執行,最后一個中間件的process_request方法最后一個執行,它的process_response方法是最先執行。

 6、process_view方法

  process_view(self, request, view_func, view_args, view_kwargs)

  該方法有四個參數

    • request是HttpRequest對象。
    • view_func是Django即將使用的視圖函數。 (它是實際的函數對象,而不是函數的名稱作為字符串。)
    • view_args是將傳遞給視圖的位置參數的列表.
    • view_kwargs是將傳遞給視圖的關鍵字參數的字典。 view_args和view_kwargs都不包含第一個視圖參數(request)。

  Django會在調用視圖函數之前調用process_view方法。

  它應該返回None或一個HttpResponse對象。 如果返回None,Django將繼續處理這個請求,執行任何其他中間件的process_view方法,然后在執行相應的視圖。 如果它返回一個HttpResponse對象,Django不會調用適當的視圖函數。 它將執行中間件的process_response方法並將應用到該HttpResponse並返回結果。

   給MD1和MD2添加process_view方法:

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")

    def process_response(self, request, response):
        print("MD1里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")

    def process_response(self, request, response):
        print("MD2里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

  訪問index視圖函數,看一下輸出結果:

MD1里面的 process_request
MD2里面的 process_request
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x000001DE68317488> index
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x000001DE68317488> index
app01 中的 index視圖
MD2里面的 process_response
MD1里面的 process_response

  process_view方法是在process_request之后,視圖函數之前執行的,執行順序按照MIDDLEWARE中的注冊順序從前到后順序執行。

7、process_exception

  process_exception(self, request, exception)

  該方法兩個參數:

    • HttpRequest對象
    • exception是視圖函數異常產生的Exception對象。

  這個方法只有在視圖函數中出現異常了才執行,它返回的值可以是一個None也可以是一個HttpResponse對象。如果是HttpResponse對象,Django將調用模板和中間件中的process_response方法,並返回給瀏覽器,否則將默認處理異常。如果返回一個None,則交給下一個中間件的process_exception方法來處理異常。它的執行順序也是按照中間件注冊順序的倒序執行。

   給MD1和MD2添加上這個方法:

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")

    def process_response(self, request, response):
        print("MD1里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD1 中的process_exception")


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")

    def process_response(self, request, response):
        print("MD2里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD2 中的process_exception")

  如果視圖函數中無異常,process_exception方法不執行。

  想辦法,在視圖函數中拋出一個異常:

def index(request):
    print("app01 中的 index視圖")
    raise ValueError("呵呵")
    return HttpResponse("O98K")

  在MD2的process_exception中返回一個響應對象:

class MD2(MiddlewareMixin):

    def process_request(self, request):
        print("MD2里面的 process_request")

    def process_response(self, request, response):
        print("MD2里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD2 中的process_exception")
        return HttpResponse(str(exception))  # 返回一個響應對象

  看輸出結果:

MD1里面的 process_request
MD2里面的 process_request
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x0000022C09727488> index
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x0000022C09727488> index
app01 中的 index視圖
呵呵
MD2 中的process_exception
MD2里面的 process_response
MD1里面的 process_response

  注意,這里並沒有執行MD1的process_exception方法,因為MD2中的process_exception方法直接返回了一個響應對象。

8、process_template_response(了解)

  process_template_response(self, request, response)

  該方法參數:

    • HttpRequest對象
    • response是TemplateResponse對象(由視圖函數或者中間件產生)。

  process_template_response是在視圖函數執行完成后立即執行,但是它有一個前提條件,那就是視圖函數返回的對象有一個render()方法(或者表明該對象是一個TemplateResponse對象或等價方法)。

class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1里面的 process_request")

    def process_response(self, request, response):
        print("MD1里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD1 中的process_exception")
        return HttpResponse(str(exception))

    def process_template_response(self, request, response):
        print("MD1 中的process_template_response")
        return response


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2里面的 process_request")

    def process_response(self, request, response):
        print("MD2里面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD2 中的process_exception")

    def process_template_response(self, request, response):
        print("MD2 中的process_template_response")
        return response

  views.py中:

def index(request):
    print("app01 中的 index視圖")

    def render():
        print("in index/render")
        return HttpResponse("O98K")
    rep = HttpResponse("OK")
    rep.render = render
    return rep

  訪問index視圖,終端輸出的結果:

MD1里面的 process_request
MD2里面的 process_request
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x000000000B46DBF8> index
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x000000000B46DBF8> index
app01 中的 index視圖
MD2 中的process_template_response
MD1 中的process_template_response
in index/render
MD2里面的 process_response
MD1里面的 process_response

  從結果看出:

    視圖函數執行完之后,立即執行了中間件的process_template_response方法,順序是倒序,先執行MD1的,在執行MD2的,接着執行了視圖函數返回的HttpResponse對象的render方法,返回了一個新的HttpResponse對象,接着執行中間件的process_response方法。

9、源碼試讀

  作為延伸擴展內容,有余力的同學可以嘗試着讀一下以下兩個自帶的中間件:

'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM