django系列8.1--django的中間件01 自定義中間件的5個方法

一.Django中的中間件

Django中間件定義:python

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.django

應用場景:瀏覽器

若是須要修改請求,例如被傳送到view中的HttpRequest對象。 或者你想修改view返回的HttpResponse對象,這些均可以經過中間件來實現。或者須要在view執行以前作一些操做,這種狀況就能夠用 middleware來實現。session

簡單來講中間件是幫助咱們在視圖函數執行以前和執行以後均可以作一些額外的操做,它本質上就是一個自定義類,類中定義了幾個方法,Django框架會在請求的特定的時間去執行這些方法。app

咱們一直都在使用中間件,只是沒有注意到而已,打開Django項目的Settings.py文件,看到下面的MIDDLEWARE配置項,django默認自帶的一些中間件:框架

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配置項是一個列表,列表中是一個個字符串,這些字符串實際上是一個個類,也就是一個個中間件。函數

<br/>測試

二.自定義中間件

在開源中國的網站中有不少關於中間件的詳細解釋,django的中間件能夠定義五個方法,主要用到process_request和process_response網站

1.中間件能夠自定義的五個方法

  • process_request(self, request)
  • process_view(self, request, view_func, view_args, view_kwargs) 在視圖函數執行以前執行
  • process_template_response(self, request, response) 在view函數執行後執行,返回對象必須有render方法
  • process_exception(self, request, exception) 在視圖函數異常時執行
  • process_response(self, request, response)

以上方法的返回值能夠是None或一個HttpResponse對象,若是是None,則繼續按照django定義的規則向後繼續執行,若是是HttpResponse對象,則直接將該對象返回給用戶。url

當用戶發起請求的時候會依次通過全部的的中間件,這個時候的請求時process_request,最後到達views的函數中,views函數處理後,在依次穿過中間件,這個時候是process_response,最後返回給請求者。

圖中的中間件都是django中的, 咱們也能夠本身定義一箇中間件.本身定義一個類,必定要繼承MiddlewareMixin

<br/>

2.自定義一箇中間件示例

1.目錄:

​ 在項目中建立一個包,隨便起名字,通常都放在一個叫作utils的包裏面,表示一個公用的組件,建立一個py文件,隨便起名字,例如叫作:middlewares.py,內容以下

from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):
    #自定義中間件,不是必需要有下面這兩個方法,有request方法說明請求來了要處理,有response方法說明響應出去時須要處理,不是非要寫這兩個方法,若是你沒寫process_response方法,那麼會一層一層的往上找,哪一個中間件有process_response方法就將返回對象給哪一個中間件
    def process_request(self, request):
        print("中間件1裏面的 process_request")

    def process_response(self, request, response):
        print("中間件1裏面的 process_response")
        return response

2.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',
    'utils.middlewares.MD1',  # 自定義中間件MD1,這個寫的是你項目路徑下的一個路徑,例如,若是你放在項目下,文件夾名成爲utils,那麼這裏應該寫utils.middlewares.MD1
    'utils.middlewares.MD2'  # 自定義中間件MD2
]

<br/>

三.中間件五種方法詳解

1.process_request

按註冊順序從上到下執行,第一個process_request方法首先執行

process_request有一個參數,就是request,這個request和視圖函數中的request是同樣的。它的返回值能夠是None也能夠是HttpResponse對象。返回值是None的話,按正常流程繼續走,交給下一個中間件處理,若是是HttpResponse對象,Django將不執行視圖函數,而將相應對象返回給瀏覽器。

<br/>

2.process_response

按註冊順序倒序執行,使用時要返回response,若是返回HttpResponse(「消息」)會將視圖函數中斷

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

<br/> 流程圖:

測試示例1(中間件return response)

這裏測試一下多箇中間件時,Django是如何執行其中的process_request和process_response方法的

utils/middlewares.py

from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):
    def process_request(self, request):
        print("中間件1裏面的 process_request")

    def process_response(self, request, response):
        print("中間件1裏面的 process_response")
        return response  # 必須有返回值,return response, response就像接力棒同樣,傳向下一層
    	#return HttpResponse("ok")  # 若是這樣寫,那麼視圖返回過來的內容就會被它替代
        
class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("中間件2裏面的 process_request")

    def process_response(self, request, response):
        print("中間件2裏面的 process_response")
        return response

在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',
    'utils.middlewares.MD1',  # 這裏註冊MD1
    'utils.middlewares.MD2',
]

訪問一個視圖,控制檯結果爲

中間件1裏面的 process_request
中間件2裏面的 process_request
app中的視圖函數
[08/Mar/2019 16:17:25] "GET /login/ HTTP/1.1" 200 528
中間件2裏面的 process_response
中間件1裏面的 process_response

由此能夠得出結論:

process_response方法是在視圖函數以後執行的,而且順序是MD1比MD2先執行。(此時settings.py中 MD2比MD1先註冊). 多箇中間件中的process_response方法是按照MIDDLEWARE中的註冊順序倒序執行的,也就是說第一個中間件的process_request方法首先執行,而它的process_response方法最後執行,最後一箇中間件的process_request方法最後一個執行,它的process_response方法是最早執行。

<br/>

測試示例2(中間件return HttpResponse(‘ok’))

process_request方法裏面不寫返回值,默認也是返回None,若是本身寫了return None,也是同樣的效果,不會中斷你的請求,可是若是return 一個httpresponse對象,那麼就會在這個方法中斷你的請求,直接返回給用戶,這就成了非正常的流程了

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Md1(MiddlewareMixin):
    
    def process_request(self,request):
        print("中間件1請求") 
        return HttpResponse("Md2中斷")  # 注意這裏,這個例子就是爲了測試這樣返回的效果

    def process_response(self,request,response):
        print("中間件1返回")
        return response

class Md2(MiddlewareMixin):

    def process_request(self,request):
        print("中間件2請求")
        
    def process_response(self,request,response):
        print("中間件2返回")
        return response

結果以下:

中間件1裏面的 process_request
中間件1裏面的 process_response

process_request方法裏面不寫返回值,默認也是返回None,若是你寫了return None,也是同樣的效果,不會中斷請求,可是若是return一個httpresponse對象,那麼就會在這個方法中斷請求,直接返回給用戶,這就成了非正常的流程了. 而且,若是你在這裏return了httpresponse對象,那麼會從你這個中間件類中的process_response方法開始執行返回操做,因此這個類裏面只要有process_response方法,確定會執行

<br/>

3.process_view

process_view方法是在process_request以後,reprocess_response以前,視圖函數以前執行的,執行順序按照MIDDLEWARE中的註冊順序從前到後順序執行的。它應該返回None或一個HttpResponse對象。 若是返回None,Django將繼續處理這個請求,執行任何其餘中間件的process_view方法,而後在執行相應的視圖。 若是它返回一個HttpResponse對象,Django不會調用對應的視圖函數。 它將執行中間件的process_response方法並將應用到該HttpResponse並返回結果。

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

該方法有四個參數   

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

<br/>

測試示例

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__) #就是url映射到的那個視圖函數,也就是說每一箇中間件的這個process_view已經提早拿到了要執行的那個視圖函數
        #ret = view_func(request) #提早執行視圖函數,不用到了上圖的試圖函數的位置再執行,若是你視圖函數有參數的話,能夠這麼寫 view_func(request,view_args,view_kwargs) 
        #return ret  #直接就在MD1中間件這裏這個類的process_response給返回了,就不會去找到視圖函數裏面的這個函數去執行了。

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

    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__)

訪問視圖函數的結果:

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

<br/>

4.process_excepiton

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

process_exception(self, request, exception)

該方法兩個參數:

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

<br/>

測試示例:

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("中間件1裏面的 process_request")

    def process_response(self, request, response):
        print("中間件1裏面的 process_response")
        return response

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

    def process_exception(self, request, exception):
        print(exception)
        print("中間件1 中的process_exception")


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("中間件2裏面的 process_request")
        pass

    def process_response(self, request, response):
        print("中間件2裏面的 process_response")
        return response

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

    def process_exception(self, request, exception):
        print(exception)
        print("中間件2 中的process_exception")

在視圖函數中拋出一個自定義異常

def index(request):
    print("app 中的 index視圖")
    raise ValueError("異常")

結果

中間件1裏面的 process_request
中間件2裏面的 process_request
--------------------------------------------------------------------------------
中間件1中的process_view
<function index at 0x00000258A097AB70> index
--------------------------------------------------------------------------------
中間件2 中的process_view
<function index at 0x00000258A097AB70> index
中間件2 中的process_exception
中間件1 中的process_exception
ValueError: 異常
中間件2裏面的 process_response
中間件1裏面的 process_response

<br/>

5.process_template_response

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

process_template_response(self, request, response)

參數: 一個HttpRequest對象,response是TemplateResponse對象(由視圖函數或者中間件產生)

<br/>

測試示例

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")

    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")
        pass

    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")

    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("OK")
    rep = HttpResponse("OK")
    rep.render = render  # 返回的對象要有一個render方法,process_template_response纔會執行
    return rep

訪問視圖以後的結果

MD1裏面的 process_request
MD2裏面的 process_request
--------------------------------------------------------------------------------
MD1 中的process_view
--------------------------------------------------------------------------------
MD2 中的process_view
app01 中的 index視圖
MD2 中的process_template_response
MD1 中的process_template_response
in index/render
MD2裏面的 process_response
MD1裏面的 process_response

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

<br/>

相關文章
相關標籤/搜索