進(jìn)入本文之前疏咐,首先請大家先看該篇博客吊说,對你開始本文的理解有一定的幫助:
一個(gè)請求request 經(jīng)過瀏覽器命咐, nginx轉(zhuǎn)發(fā)仍劈,uwsgi服務(wù)器霜幼,再到我們的應(yīng)用服務(wù)器飒房,這一過程我們暫且先跳過去赢织,因?yàn)槲覀円芯康氖且粋€(gè)請求request進(jìn)入到應(yīng)用服務(wù)器后擎鸠, 如何匹配到視圖類的(CBV)的揭鳞,進(jìn)入視圖函數(shù)前干了什么炕贵, 出了視圖函數(shù)干了什么,進(jìn)入之前與出去之后都經(jīng)過哪些中間件野崇,這些是我們著重要研究的地方称开。
1:任何一個(gè)應(yīng)用 application,他必須是可調(diào)用的(callable), 因?yàn)檫@是request的入口。源碼位置在Django中的位置如下所示:
2:所有的request都會進(jìn)入run方法:
進(jìn)入run方法以后乓梨, 就會獲取應(yīng)用的application:
3:前面說過django中的application是一個(gè)可調(diào)用對象鳖轰,那么application對應(yīng)的類就是:django.contrib.staticfiles.handlers:StaticFilesHandler 實(shí)例
最后調(diào)用父類的的方法:django.core.handlers.wsgi:WSGIHandler,至此我們已經(jīng)知道所有的request在django框架中的入口處扶镀。
好了蕴侣, 現(xiàn)在我們已經(jīng)知道每個(gè)請求的入口是WSGIHandler.__call__函數(shù), name在這個(gè)函數(shù)最重要的就是這句代碼?response =self.get_response(request)
那我們現(xiàn)在看看這句代碼的背后都干了什么臭觉,重點(diǎn)在這句:self._middleware_chain(request)昆雀,它在項(xiàng)目啟動后會將所有的中間件形成一個(gè)鏈,在鏈的最開始就是配置中MIDDLEWARE第一個(gè)中間件蝠筑。
在load_middleware方法中忆肾,get_response最初是self._get_response方法(即:最初的handler就是get_response),通過循環(huán)每次handler改變菱肖, 第一次循環(huán)后handler就是MIDDLEWARE最后一個(gè)中間件的實(shí)例客冈,一直到循環(huán)結(jié)束,那么handler就是第一個(gè)中間件的是實(shí)例稳强;
當(dāng)代碼執(zhí)行到:
self._middleware_chain(request)
當(dāng)所有的中間件都執(zhí)行完畢后场仲,最后執(zhí)行WSGIHandler的_get_response方法(如下圖)和悦, 該方法包括了,請求解析(找到正確的視圖類)渠缕,視圖函數(shù)中間件鸽素,從wrapped_callback分派執(zhí)行請求方法(get()、post()亦鳞、........)馍忽,template中間件、exception中間件燕差,最后又會執(zhí)行所有中間件的process_response方法
所以一個(gè)請求到達(dá)應(yīng)用服務(wù)器后的流程就是:
(1):request 找到應(yīng)用服務(wù)器 __call__(self, environ, start_response) 方法遭笋,并執(zhí)行 response = self.get_response(request) 方法。
(2):request 進(jìn)入?self.get_response(request) 方法后徒探,最重要的就是執(zhí)行了該句代碼:response = self._middleware_chain(request)
(3):中間件鏈 self._middleware_chain(request) 具體干了這么幾件事:
? ? ? ? ? ? (a):? 順序執(zhí)行中間件實(shí)例的 process_request 方法瓦呼,如果該方法有返回值?response 實(shí)例, 那么不會執(zhí)行后續(xù)的中間件测暗, 而是逆序執(zhí)行中間件的?process_response 方法央串,最后返回服務(wù)器直至瀏覽器
? ? ? ? ? ? (b):? ?當(dāng)所有的?process_request 方法執(zhí)行完畢后,執(zhí)行?WSGIHandler._get_response(self, request) 方法碗啄,根據(jù)請求的path解析到正確的視圖函數(shù)或視圖類(即該方法的 callback或 wrapped_callback)
? ? ? ? ? ? (c):? ?執(zhí)行所有的模板中間件
? ? ? ? ? ? (b):??執(zhí)行所有的異常中間件
? ? ? ? ? ? (d):? 最后逆序執(zhí)行中間件實(shí)例的? ?process_response 方法质和,該方法必須返回一個(gè)?response 實(shí)例 , 最后相應(yīng)返回給wsgi或uwsgi 這樣發(fā)服務(wù)器,由他們返回瀏覽器等前端
那么整個(gè)請求的過程就是在這些過程中完成的稚字,django框架的核心就是圍繞這些流程逐步拓展饲宿,請求解析、模板與響應(yīng)尉共、異常處理等等褒傅。
最后補(bǔ)充一張圖弃锐,關(guān)于請求穿過中間件的順序:
中間件順序的參考:中間件順序
def? process_request(self, request):? pass
def process_view(self, request, callback, callback_args, callback_kwargs):? pass
def?process_template_response(self, request,?response):? pass
def process_exception(self, request, exception):? pass
def process_response(self, request, response):? pass
(1): 在各個(gè)中間件的 process_request 均沒有異常且返回值是None, 那么下一步再按照中間件的順序依次執(zhí)行各自的 process_view, 如果process_view 返回值均是None, 下一步再按照中間件逆序方向從下往上執(zhí)行process_response? 袄友,直至第一個(gè)中間件的process_response? (如黑線箭頭流程所示)
(2):如果執(zhí)行的某個(gè)中間件的 process_request 返回值是HttpResponse 對象,那么直接執(zhí)行該中間件的?process_response霹菊,再按照中間件的逆序方向從下往上執(zhí)行其它的 process_response?? (如紅線箭頭流程所示)
(3):在各個(gè)中間件的 process_request 均沒有異常且返回值是None, 那么下一步再按照中間件的順序依次執(zhí)行各自的 process_view, 如果在某個(gè)中間件的process_view 返回值是?HttpResponse 對象,那么直接逆序執(zhí)行各個(gè)中間件的?process_response (從下到上)(如藍(lán)線箭頭流程所示)
(4):如果在某個(gè)中間件的 process_request 發(fā)生異常剧蚣,那么會直接該中間件的process_response?(從下到上),然后逆序執(zhí)行其它中間的?process_response? 方法(該中間件的process_response? 不會執(zhí)行)旋廷,直到結(jié)束?(如黑線箭頭流程所示)
(5):如果在某個(gè)中間件的 process_view 發(fā)生異常鸠按,那么會直接該中間件的?process_response??(從下到上),然后逆序執(zhí)行其它中間的??process_response? 方法(該中間件的?process_response? 會執(zhí)行)饶碘,直到結(jié)束??(如藍(lán)色線箭頭流程所示)
(6):如果執(zhí)行到視圖函數(shù) view 中發(fā)生異常目尖,?那么會依次執(zhí)行各中間件的?process_exception(從下到上),然后逆序執(zhí)行其它各個(gè)中間的??process_response? 方法扎运,直到結(jié)束??(如紅色線箭頭流程所示)