1、什麼是中間件呢?
2、中間件的五員大將
3、中間件的執行流程
1、什麼是中間件呢?
枯燥的中間件概念
按照官方的說法,中間件是一個用來
處理Django的請求和響應的框架級別的鉤子。它是一個輕量、低級別的插件系統,用於在全局變量中改變Django的輸入和輸入。每一箇中間件組件都負責
作一些特定的功能。但因爲其影響的是
全局,所以要謹慎使用。
那到底什麼是中間件呢?中間件其實就是幫助咱們在
Django的運行過程當中的某個環節來執行特定的操做,好比在視圖函數執行以後response一個特定的內容。它的本質是定義了一個類,
類中定義了一些方法,Django框架會在請求的特定的時間去執行這些方法。
emmm...看不懂,讓咱們來從源碼中找到答案吧!
1.讓咱們先來看一下一直在默默付出的Django自帶的中間件(setting.py中的MIDDLEWARE配置項中)
# 中間件相關的都配置在這裏
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', # session相關的中間件
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', # csrf相關的中間件
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
2.既然Django自帶有中間件,那麼咱們不妨去觀摩一下它們的本質(源碼)吧!
# 查看中間件源碼的方法:
# 1.先寫from A import B
# 2.按住Ctrl,鼠標同時點擊B
from
django.middleware.security
import
SecurityMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
from django.middleware.common import CommonMiddleware
from django.middleware.csrf import CsrfViewMiddleware
3.如下是它們的大體內容:
# 因爲源碼太多,咱們只截取大體框架,具體咱們以後再討論
# django.middleware.security.SecurityMiddleware 的源碼
class SecurityMiddleware(MiddlewareMixin):
def __init__(self, get_response=None): # 初始化
pass
def process_request(self, request): # 處理請求的實例方法
pass
def process_response(self, request, response): #處理響應的實例方法
pass
# django.contrib.sessions.middleware.SessionMiddleware 的源碼
class SessionMiddleware(MiddlewareMixin): # 定義了一個類,繼承MiddlewareMixin
def __init__(self, get_response=None): # 初始化
pass
def process_request(self, request): # 處理請求的實例方法
pass
def process_response(self, request, response): # 處理響應的實例方法
pass
# django.middleware.csrf.CsrfViewMiddleware 的源碼
class CsrfViewMiddleware(MiddlewareMixin): # 定義了一個類,繼承MiddlewareMixin
def _accept(self, request): # 私有方法
pass
def _reject(self, request, reason): # 私有方法
pass
def _get_token(self, request): # 私有方法
pass
def _set_token(self, request, response): # 私有方法
pass
def process_request(self, request): # 處理請求的實例方法
pass
def process_view(self, request, callback, callback_args, callback_kwargs): # 處理視圖的實例方法
pass
def process_response(self, request, response): # 處理響應的實例方法
pass
4.經過對Django自帶的幾個中間件源碼的大體分析,咱們能夠發現,
其實中間件就是定義了一個類,類中寫了幾個方法:好比處理響應的、處理視圖的、處理請求的..這些東西好像很固定?!好吧,接下來讓咱們來具體分析一下它的實例方法!
2、中間件的五員大將
中間件的五員大將,分別是:(主要的是process_request和process_response)
|
process_request(self,request) # 處理請求的
|
process_view(self, request, view_func, view_args, view_kwargs) # 處理視圖的
|
process_template_response(self,request,response) # 處理模板相關的
|
process_exception(self, request, exception) # 處理異常的
|
process_response(self, request, response) # 處理響應的
|
而它們的返回值能夠是
None或一個
HttpResponse對象,若是是None,則繼續按照django定義的規則
向後繼續執行,若是是HttpResponse對象,則
直接將該對象返回給用戶。
|
第一個要了解的是兩名守門大將(request 和 response)
第一步:新建一個與manage.py同級別的py文件
# mymiddleware.py
from django.utils.deprecation import MiddlewareMixin # 導入要繼承的MiddlewareMixin
from django.shortcuts import render, redirect, HttpResponse # 導入三件套
class test1(MiddlewareMixin): #定義一個名爲test1的類,繼承MiddlewareMixin
def process_request(self, request): #處理請求的實例方法
print('-----test1-----request-----') #打印用來分析test1的request、response與test2的request、response的執行順序
return #返回None,正常執行,若是返回的是HttpResponse,則不執行後面的內容,直接往回響應
def process_response(self, request, response):
print('-----test1-----response-----')
return response #經過分析源碼獲得(這裏須要本身分析)返回response是正常執行,所以,只須要中間件打印,打印以後就正常執行
class test2(MiddlewareMixin): #定義一個名爲test2的類,與test1作對比
def process_request(self, request):
print('-----test2-----request-----')
return
def process_response(self, request, response):
print('-----test2-----response-----')
return response
第二步:把新寫的中間件添加到Django的配置中
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'mymiddleware.test1', # mymiddleware.py文件下的test1類
'mymiddleware.test2', # mymiddleware.py文件下的test2類
]
第三步:運行server,查看結果並分析
-----test1-----request-----
-----test2-----request-----
-----views----- #這裏是我在views中寫了一個函數,這個函數打印-----views-----
-----test2-----response-----
-----test1-----response-----
第四步:分析
a. 首先運行test1類中處理request的實例方法,打印出了
-----test1-----request-----
b. 而後運行test2類中處理request的實例方法,打印出了
-----test2-----request-----
c. 接着運行到視圖函數中,打印出了
-----views-----
d. 而後從視圖函數中拿到response,開始往回走,因爲來的時候先通過test1,在通過test2,因此在回去的時候,要先通過test2,再通過test1,所以打印出了
-----test2-----response-----
e. 走過test2的response以後,走到test1的response,打印出了
-----test1-----response-----
f.順利走過自定義的中間件全部流程以後,拿着response經過wsgi返回給瀏覽器
第五步:總結
a. process_request() 只有一個參數request,
這個request和視圖函數中的request是同樣的。它的返回值能夠是None也能夠是HttpResponse對象。返回值是None的話,按正常流程繼續走,交給下一個中間件處理,若是是HttpResponse對象,Django將不執行視圖函數,而將相應對象返回給瀏覽器。
b. 多箇中間件中的
process_response() 方法是按照MIDDLEWARE中的
註冊順序倒序執行的。它有兩個參數,一個是request,一個是response,request是和process_request中同樣的對象,response是視圖函數返回的HttpResponse對象。該方法的返回值也必須是HttpResponse對象。
c. 中間件的
process_request方法是在執行視圖函數
以前執行的。而
process_response方法是在執行視圖函數
以後執行的。
d. 當配置多箇中間件時,會按照MIDDLEWARE中的註冊順序,也就是列表的索引值執行。
process_request是從前到後依次執行的;而process_response是從後到前依次執行的。
e.
不一樣中間件之間傳遞的request和response都是同一個對象。(所以,若是在中間件這裏截取到request和response卻並不把它交給下一個環節,那麼後面會接收不到request和response)
瞭解了兩位守門大將以後,咱們再來認識一下宮廷守衛(process_view())吧!
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)。
|
它應該返回None或一個HttpResponse對象。 若是返回None,Django將繼續處理這個請求,執行任何其餘中間件的process_view方法,而後在執行相應的視圖。 若是它返回一個HttpResponse對象,Django不會調用適當的視圖函數。 它將執行中間件的process_response方法並將應用到該HttpResponse並返回結果。
|
第一步:寫入中間件到
mymiddleware中
# mymiddleware.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, redirect, HttpResponse
class test1(MiddlewareMixin):
def process_request(self, request):
print('-----test1-----request-----')
return
def process_response(self, request, response):
print('-----test1-----response-----')
return response
def process_view(self, request, view_func, view_args, view_kwargs): #處理視圖的實例方法,它有五個參數
print("-----test1-----view-----") # 打印,用來分析執行順序
print(view_func, view_func.__name__) #一個是打印參數view_func傳遞的是什麼,另外打印它的__name__屬性,也就是函數名
class test2(MiddlewareMixin):
def process_request(self, request):
print('-----test2-----request-----')
return
def process_response(self, request, response):
print('-----test2-----response-----')
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-----test2-----view-----") #用來與test1中的view作對比
print(view_func, view_func.__name__)
第二步:運行server,查看結果並分析
-----test1-----request-----
-----test2-----request----- # 按正序運行,先運行test1的request,再運行test2的request
-----test1-----view----- # test2中的request運行完成以後接着運行test1中的view!
<function home at 0x02CCB468> home # 而後發現view_func傳遞的是一個函數的地址,這裏函數是home函數(視圖中自定義的函數,也就是request和response傳遞的對象)
-----test2-----view----- # test1中的view運行結束以後,接着運行test2中的view,原來view和request同樣,都是正向運行
<function home at 0x02CCB468> home # test1中的view和test2中的view同樣,可是想一下,這個處理view實例方法是幹嗎的呢?它出如今request運行以後,在真正運行視圖函數以前,emmmm...我也不知道這哥們是作什麼的,暫且稱它爲宮廷守衛吧!
-----views----- #這個是真正的視圖函數執行的結果
-----test2-----response----- # 接下來response按照倒序正常運行
-----test1-----response-----
第三步:分析
a. 首先運行test1類中處理request的實例方法,打印出了
-----test1-----request-----
b. 而後運行test2類中處理request的實例方法,打印出了
-----test2-----request-----
c. 接着運行test1類中處理view的實例方法,打印出了
-----test1-----view-----
d. 而後運行test2類中處理view的實例方法,打印出了
-----test2-----view-----
e. 接着運行到視圖函數中,打印出了
-----views-----
f.
而後從視圖函數中拿到response,開始往回走,因爲來的時候先通過test1,在通過test2,因此在回去的時候,要先通過test2,再通過test1,所以打印出了
-----test2-----response-----
g. 走過test2的response以後,走到test1的response,打印出了
-----test1-----response-----
h. 順利走過自定義的中間件全部流程以後,拿着response經過wsgi返回給瀏覽器
第四步:總結
a. process_view方法是在
process_request以後,視圖函數以前執行的,執行順序按照MIDDLEWARE中的
註冊順序從前到後順序執行的
b. 它返回None或一個HttpResponse對象。 若是返回None,Django將繼續處理這個請求,執行任何其餘中間件的process_view方法,而後在執行相應的視圖。 若是它返回一個HttpResponse對象,Django不會調用適當的視圖函數。 它將執行中間件的process_response方法並將應用到該HttpResponse並返回結果。
接下來要登場的是皇家守衛中..霸氣側漏大名鼎鼎的——醫療兵(processe_exception)(:D)
process_exception的我的簡介
|
process_exception(self, request, exception)
|
request是HttpRequest對象。
|
exception是視圖函數異常產生的Exception對象。
|
這個方法
只有在視圖函數中出現異常了才執行,它返回的值能夠是一個None也能夠是一個HttpResponse對象。若是是HttpResponse對象,Django將調用模板和中間件中的process_response方法,並返回給瀏覽器,不然將默認處理異常。若是返回一個None,則交給下一個中間件的process_exception方法來處理異常。它的執行順序也是按照中間件註冊順序的倒序執行。
|
第一步:寫入中間件到
mymiddleware中
# mymiddleware.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, redirect, HttpResponse
class test1(MiddlewareMixin):
def process_request(self, request):
print('-----test1-----request-----')
return
def process_response(self, request, response):
print('-----test1-----response-----')
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-----test1-----view-----")
print(view_func, view_func.__name__)
def process_exception(self, request, exception): # 處理異常的實例方法,有兩個參數
print(exception) # 打印exception這個參數,猜想是異常信息
print("-----test1-----exception-----") # 打印,分析運行順序
return HttpResponse(str(exception)) #因爲拋出自定義異常會報錯,咱們在這裏把異常截獲並改變response
class test2(MiddlewareMixin):
def process_request(self, request):
print('-----test2-----request-----')
return
def process_response(self, request, response):
print('-----test2-----response-----')
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-----test2-----view-----")
print(view_func, view_func.__name__)
def process_exception(self, request, exception):
print(exception) # 用來分析是否與test1中是同一個異常
print("-----test2-----exception-----") # 用來與test1作對比
第二步:因爲process_exception只有當遇到視圖函數中拋出異常時才作處理,所以咱們手動拋出一個異常
# views.py
from django.shortcuts import render, redirect, HttpResponse
# Create your views here.
def home(request):
print('-----views-----')
raise ValueError("自定義異常") #拋出自定義異常,ValueError
return HttpResponse('-----home-----')
第三步:運行server,查看結果並分析
-----test1-----request-----
-----test2-----request-----
-----test1-----view-----
<function home at 0x02CCB468> home
-----test2-----view-----
<function home at 0x02CCB468> home
-----views----- # 正常運行到視圖函數中打印的部分
自定義異常 # 參數傳遞的是自定義異常
-----test2-----exception----- # 能夠看到先運行了test2中的exception
自定義異常 # test1和test2中的異常是一個異常,能夠推斷出,test2中捕獲異常以後,把異常傳遞給了test1
-----test1-----exception----- # process_exctption是倒序運行
-----test2-----response-----
-----test1-----response-----
第四步:分析
a. 首先運行test1類中處理request的實例方法,打印出了
-----test1-----request-----
b. 而後運行test2類中處理request的實例方法,打印出了
-----test2-----request-----
c. 接着運行test1類中處理view的實例方法,打印出了
-----test1-----view-----
d. 而後運行test2類中處理view的實例方法,打印出了
-----test2-----view-----
e. 接着運行到視圖函數中,打印出了
-----views-----
f. 而後因爲視圖函數拋出異常,運行到test2類中處理exception的實例方法,打印出了
-----test2-----exception-----
g. 接着運行到test1中處理exception的實例方法,打印出了
-----test1-----exception-----
h. 而後從視圖函數中拿到response,開始往回走,因爲來的時候先通過test1,在通過test2,因此在回去的時候,要先通過test2,再通過test1,所以打印出了
-----test2-----response-----
i. 走過test2的response以後,走到test1的response,打印出了
-----test1-----response-----
j. 順利走過自定義的中間件全部流程以後,拿着response經過wsgi返回給瀏覽器
第五步:總結
a.
若是視圖函數中無異常,process_exception方法不執行。
b. 這裏醫療兵也很容易理解對吧?(:D)
介紹了以上四員大將以後,最後一個要出場的傢伙露面的機會很少(使用較少),但仍是要介紹對吧!
process_template_response的我的簡介
|
process_template_response(self, request, response)process_view(self, request, view_func, view_args, view_kwargs)
|
request是HttpRequest對象。
|
response是TemplateResponse對象(由視圖函數或者中間件產生)。
|
view_args是將傳遞給視圖的位置參數的列表。
|
view_kwargs是將傳遞給視圖的關鍵字參數的字典。 view_args和view_kwargs都不包含第一個視圖參數(request)。
|
process_template_response是在視圖函數執行完成後當即執行,可是它有一個前提條件,那就是視圖函數返回的對象有一個render()方法(或者代表該對象是一個TemplateResponse對象或等價方法)。
|
第一步:寫入中間件到mymiddleware中
# mymiddleware.py
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import render, redirect, HttpResponse
class test1(MiddlewareMixin):
def process_request(self, request):
print('-----test1-----request-----')
return
def process_response(self, request, response):
print('-----test1-----response-----')
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-----test1-----view-----")
print(view_func, view_func.__name__)
def process_exception(self, request, exception):
print(exception)
print("-----test1-----exception-----")
return HttpResponse(str(exception))
def process_template_response(self, request, response): # process_template_response實例化對象,有兩個參數
print("-----test1-----template-----") #打印
,用來分析順序
return response #把response原路返回
class test2(MiddlewareMixin):
def process_request(self, request):
print('-----test2-----request-----')
return
def process_response(self, request, response):
print('-----test2-----response-----')
return response
def process_view(self, request, view_func, view_args, view_kwargs):
print("-----test2-----view-----")
print(view_func, view_func.__name__)
def process_exception(self, request, exception):
print(exception)
print("-----test2-----exception-----")
def process_template_response(self, request, response):
print("-----test2-----template-----") #用來與test1中的process_template_response作對比
return response
第二步:因爲process_template_response的觸發須要必定的條件(視圖函數返回的對象有一個render()方法)
# views.py
from django.shortcuts import render, redirect, HttpResponse
# Create your views here.
def home(request):
print('-----views-----')
def render():
print("-----render-----")
return HttpResponse("home")
rep = HttpResponse("OK")
rep.render = render # 這步是經過看源碼發現能夠在外部給它添加實例化方法
return rep
第三步:運行server,查看結果並分析
-----test1-----request-----
-----test2-----request-----
-----test1-----view-----
<function home at 0x02CCB0C0> home
-----test2-----view-----
<function home at 0x02CCB0C0> home
-----views----- # 打印函數中的views
-----test2-----template----- # 打印了test2中的process_template_response
-----test1-----template----- # 打印了test1中的process_template_response
-----render----- # 有意思的地方在這裏,中間件執行完以後,又回到視圖函數中執行render中的內容
-----test2-----response-----
-----test1-----response-----
第四步:分析
a. 首先運行test1類中處理request的實例方法,打印出了-----test1-----request-----
b. 而後運行test2類中處理request的實例方法,打印出了-----test2-----request-----
c. 接着運行test1類中處理view的實例方法,打印出了-----test1-----view-----
d. 而後運行test2類中處理view的實例方法,打印出了-----test2-----view-----
e. 接着運行到視圖函數中,打印出了-----views-----
f. 而後跳過render函數,運行test2類中的處理template_response的實例方法,打印出了
-----test2-----template-----
g. 接着運行test1類中的處理template_response的實例方法,打印出了
-----test1-----template-----
h. 而後回過頭去運行render函數,打印出了
-----render-----
i. 而後從視圖函數中拿到response,開始往回走,因爲來的時候先通過test1,在通過test2,因此在回去的時候,要先通過test2,再通過test1,所以打印出了-----test2-----response-----
j. 走過test2的response以後,走到test1的response,打印出了-----test1-----response-----
k. 順利走過自定義的中間件全部流程以後,拿着response經過wsgi返回給瀏覽器
第五步:總結
a.
視圖函數執行完以後,當即執行了中間件的process_template_response方法,順序是
倒序,先執行MD1的,在執行MD2的,
接着執行了視圖函數返回的HttpResponse對象的render方法,返回了一個新的HttpResponse對象,接着執行中間件的process_response方法。
3、中間件的執行順序
1.request和response兩兄弟的執行順序
請求到達中間件以後,先按照正序執行每一個註冊中間件的process_reques方法,process_request方法返回的值是None,就依次執行,
若是返回的值是HttpResponse對象,再也不執行後面的process_request方法,而是執行當前對應中間件的process_response方法,將HttpResponse對象返回給瀏覽器。
也就是說:如MIDDLEWARE中註冊了6箇中間件,執行過程當中,
第3箇中間件返回了一個HttpResponse對象,那麼第4,5,6中間件的process_request和process_response方法都不執行,
順序執行3,2,1中間件的process_response方法。
2.兩員守門大將(request、response)和宮廷守衛(view)的執行順序
process_request方法都執行完後,匹配路由,找到要執行的視圖函數,
先不執行視圖函數,先執行中間件中的process_view方法,process_view方法返回None,繼續按順序執行,全部process_view方法執行完後執行視圖函數。
假如
中間件3 的process_view方法返回了HttpResponse對象,
則4,5,6的process_view以及視圖函數都不執行,直接從最後一箇中間件,也就是中間件6的process_response方法開始倒序執行。
3.那麼醫療兵(exception)和隱者(template)與它們之間又有什麼關係呢?
它們兩個須要必定的觸發條件:
exception須要視圖函數拋出一個異常;template須要視圖函數返回的對象中有一個render()方法。
a. 從下圖能夠看出,request若是返回none,則按照正常順序從上到下(註冊的順序)依次執行;
b. 若是在某個中間件中的request中出現了HttpResponse,那麼就從這個中間件的處理response的方法倒序往回執行;
c. request和view都是返回None才正常執行,而response是返回response才正常執行;
d. 若是某個中間件的處理視圖的實例化方法返回了HttpResponse,
那麼就再也不執行後續中間件的處理view的方法,而是直接走處處理response,按照倒序往回執行
a. 下面箭頭向下的有request和view,說明只有它們兩個是從上往下正常執行的;
b. template和response用了虛線,表示二者的觸發須要必定的條件;
4.對比一下皇家守衛團的成員們!
|
執行順序
|
什麼時候執行
|
返回值
|
process_request
|
按照註冊順序
|
請求從wsgi拿到以後
|
返回None,繼續執行後續的中間件的process_request方法
返回response , 不執行後續的中間件的process_request方法
|
process_response
|
按照註冊順序的倒序
|
請求有響應的時候
|
必須返回一個response對象
|
process_view
|
按照註冊順序
|
在urls.py中找到對應關係以後
在執行真正的視圖函數以前
|
返回None,繼續執行後續的中間件的process_view方法
返回response,不執行後續中間件的process_view方法,
從最後一個process_response返回
|
process_exception
|
按照註冊順序的倒序
|
視圖函數中拋出異常的時候才執行
|
返回None,繼續執行後續中間件的process_exception
返回response,
|
process_template_response
|
按照註冊順序的倒序
|
視圖函數執行完,
在執行視圖函數返回的響應對象的render方法以前
|
返回None,繼續執行後續中間件的process_exception
返回response,
|
5.接下來讓咱們來完整的看一下Django的流程吧!