中間件是一個(gè)介入Django的請(qǐng)求和響應(yīng)的處理過程中的鉤子框架。它是一個(gè)輕量級(jí),底層的“插件”系統(tǒng),用于在全局修改Django的輸入或輸出。
中間件組件責(zé)任處理某些特殊的功能。例如,Django包含一個(gè)中間件組件,AuthenticationMiddleware ,使用會(huì)話將用戶和請(qǐng)求關(guān)聯(lián)。
這篇文檔講解了中間件如何工作,如何激活中間件,以及如何編寫自己的中間件。Django集成了一些內(nèi)置的中間件可以直接開箱即用。它們被歸檔在 內(nèi)置中間件參考.
要激活一個(gè)中間件組件,需要把它添加到你Django配置文件中的MIDDLEWARE_CLASSES 列表中。
在MIDDLEWARE_CLASSES中,每一個(gè)中間件組件用字符串的方式描述:一個(gè)完整的Python全路徑加上中間件的類名稱。例如,使用 django-admin startproject創(chuàng)建工程的時(shí)候生成的默認(rèn)值:
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
)
Django的程序中,中間件不是必需的 — 只要你喜歡,MIDDLEWARE_CLASSES可以為空 — 但是強(qiáng)烈推薦你至少使用CommonMiddleware。
MIDDLEWARE_CLASSES中的順序非常重要,因?yàn)橐粋€(gè)中間件可能依賴于另外一個(gè)。例如,AuthenticationMiddleware在會(huì)話中儲(chǔ)存已認(rèn)證的用戶。所以它必須在SessionMiddleware之后運(yùn)行。一些關(guān)于Django中間件類的順序的常見提示,請(qǐng)見Middleware ordering。
在請(qǐng)求階段中,調(diào)用視圖之前,Django會(huì)按照MIDDLEWARE_CLASSES中定義的順序自頂向下應(yīng)用中間件。會(huì)用到兩個(gè)鉤子:
在響應(yīng)階段中,調(diào)用視圖之后,中間件會(huì)按照相反的順序應(yīng)用,自底向上。會(huì)用到三個(gè)鉤子:
如果你愿意的話,你可以把它想象成一顆洋蔥:每個(gè)中間件都是包裹視圖的一層“皮”。
每個(gè)鉤子的行為接下來會(huì)描述。
編寫自己的中間件很容易的。每個(gè)中間件組件是一個(gè)單獨(dú)的Python的class,你可以定一個(gè)或多個(gè)下面的這些方法:
process_request(request)
request是一個(gè)HttpRequest 對(duì)象。
在Django決定執(zhí)行哪個(gè)視圖(view)之前,process_request()會(huì)被每次請(qǐng)求調(diào)用。
它應(yīng)該返回一個(gè)None 或一個(gè)HttpResponse對(duì)象。如果返回 None, Django會(huì)繼續(xù)處理這個(gè)請(qǐng)求,執(zhí)行其他process_request()中間件,然后process_view()中間件顯示對(duì)應(yīng)的視圖。如果它返回一個(gè)HttpResponse對(duì)象,Django便不再會(huì)去調(diào)用其他的請(qǐng)求(request), 視圖(view)或其他中間件,或?qū)?yīng)的視圖;處理HttpResponse的中間件會(huì)處理任何返回的響應(yīng)(response)。
process_view(request, view_func, view_args, view_kwargs)
request是一個(gè)HttpRequest對(duì)象。view_func是 Django會(huì)調(diào)用的一個(gè)Python的函數(shù)。(它確實(shí)是一個(gè)函數(shù)對(duì)象,不是函數(shù)的字符名稱。) view_args是一個(gè)會(huì)被傳遞到視圖的位置參數(shù)列表,而view_kwargs 是一個(gè)會(huì)被傳遞到視圖的關(guān)鍵字參數(shù)字典。 view_args和 view_kwargs 都不包括第一個(gè)視圖參數(shù)(request)。
process_view()會(huì)在Django調(diào)用視圖(view)之前被調(diào)用。
它將返回None 或一個(gè)HttpResponse 對(duì)象。如果返回 None,將會(huì)繼續(xù)處理這個(gè)請(qǐng)求,執(zhí)行其他的process_view() 中間件,然后顯示對(duì)應(yīng)的視圖。如果返回HttpResponse對(duì)象,Django就不再會(huì)去調(diào)用其他的視圖(view),異常中間件(exception middleware)或?qū)?yīng)的視圖 ;它會(huì)把響應(yīng)中間件應(yīng)用到HttpResponse上,并返回結(jié)果。
注意
在中間件內(nèi)部,從process_request或者process_view方法中訪問request.POST或者request.REQUEST將會(huì)阻礙該中間 件之后的所有視圖無法修改request的上傳處理程序, 一般情況要避免這樣使用。
類CsrfViewMiddleware可以被認(rèn)為是個(gè)例外 ,因?yàn)樗峁┝薱srf_exempt() 和 csrf_protect()兩個(gè)允許視圖來精確控制 在哪個(gè)點(diǎn)需要開啟CSRF驗(yàn)證。
process_template_response(request, response)
request是一個(gè)HttpRequest對(duì)象。response是一個(gè)TemplateResponse對(duì)象(或等價(jià)的對(duì)象),由Django視圖或者中間件返回。
如果響應(yīng)的實(shí)例有render()方法,process_template_response()在視圖剛好執(zhí)行完畢之后被調(diào)用,這表明了它是一個(gè)TemplateResponse對(duì)象(或等價(jià)的對(duì)象)。
這個(gè)方法必須返回一個(gè)實(shí)現(xiàn)了render方法的響應(yīng)對(duì)象。它可以修改給定的response對(duì)象,通過修改 response.template_name和response.context_data或者它可以創(chuàng)建一個(gè)全新的 TemplateResponse或等價(jià)的對(duì)象。
你不需要顯式渲染響應(yīng) —— 一旦所有的模板響應(yīng)中間件被調(diào)用,響應(yīng)會(huì)自動(dòng)被渲染。
在一個(gè)響應(yīng)的處理期間,中間件以相反的順序運(yùn)行,這包括process_template_response()。
process_response(request, response)
request是一個(gè)HttpRequest對(duì)象。response是Django視圖或者中間件返回的HttpResponse或者StreamingHttpResponse對(duì)象。
process_response()在所有響應(yīng)返回瀏覽器之前被調(diào)用。
這個(gè)方法必須返回HttpResponse或者StreamingHttpResponse對(duì)象。它可以改變已有的response,或者創(chuàng)建并返回新的HttpResponse或StreamingHttpResponse對(duì)象。
不像 process_request()和process_view()方法,即使同一個(gè)中間件類中的process_request()和process_view()方法會(huì)因?yàn)榍懊娴囊粋€(gè)中間件返回HttpResponse而被跳過,process_response()方法總是會(huì)被調(diào)用。特別是,這意味著你的process_response()方法不能依賴于process_request()方法中的設(shè)置。
最后,記住在響應(yīng)階段中,中間件以相反的順序被應(yīng)用,自底向上。意思是定義在MIDDLEWARE_CLASSES最底下的類會(huì)最先被運(yùn)行。
不像HttpResponse,StreamingHttpResponse并沒有content屬性。所以,中間件再也不能假設(shè)所有響應(yīng)都帶有content屬性。如果它們需要訪問內(nèi)容,他們必須測試是否為流式響應(yīng),并相應(yīng)地調(diào)整自己的行為。
if response.streaming:
response.streaming_content = wrap_streaming_content(response.streaming_content)
else:
response.content = alter_content(response.content)
注意
我們需要假設(shè)streaming_content可能會(huì)大到在內(nèi)存中無法容納。響應(yīng)中間件可能會(huì)把它封裝在新的生成器中,但是一定不要銷毀它。封裝一般會(huì)實(shí)現(xiàn)成這樣:
def wrap_streaming_content(content): for chunk in content: yield alter_content(chunk)
process_exception(request, exception)
request是一個(gè)HttpRequest對(duì)象。exception是一個(gè)被視圖中的方法拋出來的 Exception對(duì)象。
當(dāng)一個(gè)視圖拋出異常時(shí),Django會(huì)調(diào)用process_exception()來處理。process_exception()應(yīng)該返回一個(gè)None 或者一個(gè)HttpResponse對(duì)象。如果它返回一個(gè)HttpResponse對(duì)象,模型響應(yīng)和響應(yīng)中間件會(huì)被應(yīng)用,響應(yīng)結(jié)果會(huì)返回給瀏覽器。Otherwise, default exception handling kicks in.
再次提醒,在處理響應(yīng)期間,中間件的執(zhí)行順序是倒序執(zhí)行的,這包括process_exception。如果一個(gè)異常處理的中間件返回了一個(gè)響應(yīng),那這個(gè)中間件上面的中間件都將不會(huì)被調(diào)用。
大多數(shù)的中間件類都不需要一個(gè)初始化方法,因?yàn)橹虚g件的類定義僅僅是為process_*提供一個(gè)占位符。如果你確實(shí)需要一個(gè)全局的狀態(tài)那就可以通過__init__來加載。然后要銘記如下兩個(gè)警告:
Django初始化你的中間件無需任何參數(shù),因此不要定義一個(gè)有參數(shù)的__init__方法。 不像process_*每次請(qǐng)求到達(dá)都要調(diào)用__init__只會(huì)被調(diào)用一次,就是在Web服務(wù)啟動(dòng)的時(shí)候。
有時(shí)在運(yùn)行時(shí)決定是否一個(gè)中間件需要被加載是很有用的。 在這種情況下,你的中間件中的 __init__方法可以拋出一個(gè)django.core.exceptions.MiddlewareNotUsed異常。Django會(huì)從中間件處理過程中移除這部分中間件,并且當(dāng)DEBUG為True的時(shí)候在django.request記錄器中記錄調(diào)試信息。
1.8中的修改:
之前 MiddlewareNotUsed異常不會(huì)被記錄。