Django的中間件以及Request/Response循環(huán)
前言:萌新的疑問
在建立一個新的Django項目時,你要做的第一件事就是連接你的URLconf并設(shè)置一些視圖。 但是這里真的發(fā)生了什么? Django如何將流量路由到視圖,中間件在這個周期中扮演什么角色?
我以前眼中的Django:

WSGI
WSGI是為解決一個基本問題而創(chuàng)建的工具:將Web服務器連接到Web框架。 WSGI有兩個方面:'服務器'方面和'應用程序'方面。 為了處理WSGI響應,服務器執(zhí)行應用程序,并向應用程序端提供回調(diào)函數(shù)。 應用程序處理請求并使用提供的回調(diào)將響應返回給服務器。 本質(zhì)上,WSGI處理程序充當您的Web服務器(Apache,NGINX等)和您的Django項目之間的守門員。
服務器和應用程序之間是中間件。 您可以將中間件視為一系列雙向過濾器:它們可以改變(或短路)在網(wǎng)絡(luò)和Django應用程序之間來回傳輸?shù)臄?shù)據(jù)。
整個Django的執(zhí)行過程:數(shù)據(jù)流
當用戶請求你的應用程序時,WSGI處理程序被實例化,然后Django按照順序處理以下環(huán)節(jié):
- 導入你的settings.py文件和Django的異常類。
- 加載它在位于settings.py中的MIDDLEWARE_CLASSES或者。MIDDLEWARES(取決于Django版本)元組中找到的所有中間件類。
- 構(gòu)建處理 request,view, response和exception的四個方法列表。
- 通過請求方法循環(huán),按順序運行它們。
- 解決了所請求的URL。
- 循環(huán)每個視圖處理方法。
- 調(diào)用視圖函數(shù)(通常是渲染模板)。
- 處理任何異常方法。
- 循環(huán)遍歷每個響應方法(從內(nèi)向外,從請求中間件的相反順序)。
- 最后建立一個返回值并調(diào)用web服務器的回調(diào)函數(shù)。
中間件
中間件被用在Django項目中的許多關(guān)鍵功能中:例如,使用CSRF中間件來防止跨站請求偽造攻擊。 他們用來處理會話數(shù)據(jù)。 身份驗證和授權(quán)是使用中間件完成的。 您可以編寫自己的中間件類來通過應用程序調(diào)整(或短路)數(shù)據(jù)流。

process_request
Django中間件必須至少包含以下方法之一:process_request,process_response,process_view 和 process_exception。 這些是由WSGI處理程序收集的方法,然后按列出的順序調(diào)用。 讓我們快速瀏覽django.contrib.auth.middleware.AuthenticationMiddleware,這是運行django-admin.py startproject時默認安裝的中間件之一:
def get_user(request):
if not hasattr(request, '_cached_user'):
request._cached_user = auth.get_user(request)
return request._cached_user
class AuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
assert hasattr(request, 'session'), (
"The Django authentication middleware requires session middleware "
"to be installed. Edit your MIDDLEWARE%s setting to insert "
"'django.contrib.sessions.middleware.SessionMiddleware' before "
"'django.contrib.auth.middleware.AuthenticationMiddleware'."
) % ("_CLASSES" if settings.MIDDLEWARE is None else "")
request.user = SimpleLazyObject(lambda: get_user(request))
正如你所看到的,這個中間件只能處理來自Django應用程序的數(shù)據(jù)流的“請求”步驟。 該中間件首先驗證會話中間件是否已經(jīng)被使用,然后通過調(diào)用get_user幫助函數(shù)來設(shè)置用戶。 當WSGI處理程序迭代process_request方法列表時,它會構(gòu)建這個請求對象,最終將被傳遞到視圖中,并且您將能夠引用request.user。 settings.py中有一些中間件沒有有process_request方法。 不過沒關(guān)系,在這個階段剛剛被跳過。
process_request應該返回None(如本例),或者可以返回一個HttpResponse對象。 在前一種情況下,WSGI處理程序?qū)⒗^續(xù)處理process_request方法,后者將“短路”進程并開始process_response循環(huán)。【如上圖所示,跳過view過程,直接去response】
Resolve the URL(解析URL)
現(xiàn)在process_request方法已經(jīng)被調(diào)用了,現(xiàn)在我們有一個請求對象將被傳遞給視圖。在此之前,Django必須解析URL并確定調(diào)用哪個視圖函數(shù)。這只是通過正則表達式匹配來完成的。您的settings.py將會有一個名為ROOT_URLCONF的鍵,它表示“root”urls.py文件,您將從中為每個應用程序添加urls.py文件。 URL路由在Django教程中已經(jīng)非常廣泛地介紹了,所以在這里就不需要了。
一個觀點有三個要求:
- 它必須是可調(diào)用的。它可以是基于函數(shù)的視圖,也可以是繼承自基于類的視圖。根據(jù)HTTP動作(GET,POST等)查看as_view() 方法以使其可調(diào)用。
- 它必須接受一個HttpRequest對象作為第一個位置參數(shù)。這是調(diào)用所有process_request和process_view中間件方法的結(jié)果。
- 它必須返回一個HttpResponse對象,或引發(fā)一個異常。這是用于啟動WSGI處理程序的process_view循環(huán)的響應對象。
process_view
現(xiàn)在WSGI處理程序知道要調(diào)用哪個視圖函數(shù),它再次遍歷其中間件方法列表。 任何Django中間件的process_view方法都是這樣聲明的:
process_view(request,view_function,view_args,view_kwargs)
和process_request一樣,process_view函數(shù)必須返回None或HttpResponse對象(或引發(fā)異常),從而允許WSGI處理程序繼續(xù)處理視圖或“短路”并返回響應。 查看CSRF中間件的源代碼,查看process_view的實例。如果存在CSRF cookie,那么process_view方法將返回None并執(zhí)行視圖。 如果不是,請求被拒絕,并且進程被短路,導致失敗消息。
process_exception
如果view函數(shù)產(chǎn)生一個異常,Handler將遍歷它的process_exception方法列表。 這些方法以相反的順序執(zhí)行,從settings.py中列出的最后一個中間件到第一個。 如果發(fā)生異常,則進程將短路,并且不會調(diào)用其他進程中斷件。 通常我們依靠Django的BaseHandler提供的異常處理程序,但是在編寫自己的定制中間件類時,您當然可以實現(xiàn)自己的異常處理。
process_response
在這一點上,我們將有一個HttpResponse對象,可以是由視圖或由WSGI處理程序構(gòu)建的process_view方法列表返回的,也可以輪流循環(huán)訪問響應中間件。這是任何中間件都必須修改數(shù)據(jù)的最后機會,并且是從內(nèi)層向外執(zhí)行的(想象一下洋蔥,視圖在中心)。看一下緩存中間件源代碼中的process_response實例:取決于應用程序中的不同條件(即緩存是否關(guān)閉,如果我們正在處理流等),我們需要響應存儲在緩存中還是不存在。
注意:在1.10之前的Django和更高版本之間的一個區(qū)別是:在舊式MIDDLEWARE_CLASSES中,即使較早的中間件使進程短路,每個中間件也將始終調(diào)用其process_response方法。在新的MIDDLEWARES風格中,只有中間件和之前執(zhí)行的中間件才會調(diào)用其process_response方法。有關(guān)MIDDLEWARES和MIDDLEWARE_CLASSES之間差異的更多詳細信息,請參閱文檔。
好了~
最后,Django的WSGI Handler從HttpResponse對象構(gòu)建一個返回值,并執(zhí)行回調(diào)函數(shù)將該數(shù)據(jù)發(fā)送到Web服務器并發(fā)送給用戶。
所以,兩個關(guān)鍵要點:
- 現(xiàn)在我們知道view函數(shù)是如何與URLconf相匹配的,以及實際調(diào)用的是什么(WSGI Handler)。
- 有四個關(guān)鍵點可以通過您自己的定制中間件進入請求/響應循環(huán):process_request,process_response,process_view和process_exception。 想一想:請求中間件是從外部執(zhí)行的,在中心點擊查看,然后通過響應中間件返回到表面。