一、中間件的概念
中間件顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,而且在全局上改變django的輸入與輸出。由於改變的是全局,因此須要謹慎實用,用很差會影響到性能。python
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來實現。session
Django默認的Middleware:app
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以後就再也不註釋這個中間件了。post
那接下來就爲你們解開神祕的面紗,深刻探索一下。學習中間件中的方法以及這些方法何時被執行。性能
二、自定製中間件
中間件能夠定義五個方法,分別是:學習
-
- 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)(瞭解)
三、自定製中間件示例
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
四、process_request方法
process_request有一個參數,就是request,這個request和視圖函數中的request是同樣的。spa
它的返回值能夠是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都是同一個對象
五、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方法是最早執行。
六、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中的註冊順序從前到後順序執行。
七、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方法直接返回了一個響應對象。
八、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方法。
九、源碼試讀
做爲延伸擴展內容,有餘力的同窗能夠嘗試着讀一下如下兩個自帶的中間件:
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',