中間件是一個鉤子框架,是介於request與response處理之間的一道處理過程,它們能夠介入Django 的請求和響應處理過程。它是一個輕量級、底層的「插件」系統,用於在全局修改Django 的輸入或輸出。html
每一箇中間件組件負責完成某個特定的功能。例如,Django 包含的一箇中間件組件AuthenticationMiddleware ,它使用會話將用戶和請求關聯起來。 python
咱們從瀏覽器發出一個請求 Request,獲得一個響應後的內容 HttpResponse ,這個請求傳遞到 Django的過程以下:django
要激活一箇中間件組件,須要把它添加到Django 配置文件中的MIDDLEWARE_CLASSES 元組中。瀏覽器
在MIDDLEWARE_CLASSES中,每個中間件組件用字符串的方式描述:一個完整的Python全路徑加上中間件的類名稱。例如,使用 django-admin startproject建立工程的時候生成的默認值:服務器
1
2
3
4
5
6
7
8
9
10
|
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能夠爲空 —— 可是強烈推薦你至少使用CommonMiddleware。session
MIDDLEWARE_CLASSES中的順序很是重要,由於一箇中間件可能依賴於另一個。例如,AuthenticationMiddleware在會話中儲存已認證的用戶。因此它必須在SessionMiddleware以後運行。一些關於Django中間件類的順序的常見提示,請見中間件排序。框架
在請求階段中,調用視圖以前,Django會按照MIDDLEWARE_CLASSES中定義的順序自頂向下應用中間件。會用到兩個鉤子:ide
在響應階段中,調用視圖以後,中間件會按照相反的順序應用,自底向上。會用到三個鉤子:函數
若是你願意的話,你能夠把它想象成一顆洋蔥:每一箇中間件都是包裹視圖的一層「皮」。測試
每一個鉤子的行爲接下來會描述。
#---------------------------------------------views.py def hello(req): print('views') return HttpResponse('ok') #---------------------------------------------settings.py MIDDLEWARE_CLASSES = [ 'mymiddle_ware.mymiddle.MiddleTest', 'mymiddle_ware.mymiddle.MiddleTest2', 'django.middleware.security.SecurityMiddleware', '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', ] #---------------------------------------------mymiddle.py from django.shortcuts import render,HttpResponse class MiddleTest(object): def process_request(self,req): print('process_request....') # return HttpResponse('你好') #越過其它中間件和views,去通過response系列轉運 def process_response(self,req,res): print('process_response....') return res ##res即views返回的 HttpResponse('ok')對象 class MiddleTest2(object): def process_request(self,req): print('process_request2....') def process_response(self,req,res): print('process_response2....') return res #res即views返回的 HttpResponse('ok')對象 def process_exception(self,request, exception): print('exception',exception) return HttpResponse(exception)
編寫本身的中間件很容易的。每一箇中間件組件是一個獨立的Python 類,你能夠定義下面這些方法中的一個或多個:
request是一個HttpRequest 對象。
在Django決定執行哪一個視圖以前,process_request()會在每一個請求上調用。
它應該返回一個None 或一個HttpResponse對象。若是返回None,Django會繼續處理這個請求,執行其它process_request()中間件,而後process_view()中間件,最後是對應的視圖。若是它返回一個HttpResponse對象,Django 就不用再去調用其它的request、view 或exception 中間件,或對應的視圖;它將對HttpResponse 運用響應階段的中間件,並返回結果。
request是一個HttpRequest對象。view_func是 Django會調用的一個Python的函數。(它是一個真實的函數對象,不是函數的字符名稱。) view_args是一個會被傳遞到視圖的位置參數列表,而view_kwargs 是一個會被傳遞到視圖的關鍵字參數字典。 view_args和 view_kwargs 都不包括第一個視圖參數(request)。
process_view()會在Django 調用視圖以前被調用。
它將返回None 或一個HttpResponse 對象。若是返回None,Django 將會繼續處理這個請求,執行其它的process_view() 中間件,而後調用對應的視圖。若是返回一個HttpResponse對象,Django 就不用再去調用其它的view 或exception 中間件,或對應的視圖;它將對HttpResponse 運用響應階段的中間件,並返回結果。
注意
在中間件內部,從process_request 或process_view 中訪問request.POST 或request.REQUEST 將阻礙該中間件以後的全部視圖沒法修改請求的上傳處理程序,通常狀況下要避免這樣使用。
類CsrfViewMiddleware能夠被認爲是個例外,由於它提供csrf_exempt() 和csrf_protect()兩個裝飾器,容許視圖顯式控制在哪一個點須要開啓CSRF驗證。
process_template_response(request, response)
request是一個HttpRequest對象。response是一個TemplateResponse對象(或等價的對象),由Django視圖或者中間件返回。
若是響應的實例有render()方法,process_template_response()在視圖恰好執行完畢以後被調用,這代表了它是一個TemplateResponse對象(或等價的對象)。
這個方法必須返回一個實現了render方法的響應對象。它能夠修改給定的response對象,經過修改 response.template_name和response.context_data或者它能夠建立一個全新的 TemplateResponse或等價的對象。
你不須要顯式渲染響應 —— 一旦全部的模板響應中間件被調用,響應會自動被渲染。
在一個響應的處理期間,中間件以相反的順序運行,這包括process_template_response()。
process_response(request, response)
request是一個HttpRequest對象。response是Django視圖或者中間件返回的HttpResponse或者StreamingHttpResponse對象。
process_response()在全部響應返回瀏覽器以前被調用。
這個方法必須返回HttpResponse或者StreamingHttpResponse對象。它能夠改變已有的response,或者建立並返回新的HttpResponse或StreamingHttpResponse對象。
不像 process_request()和process_view()方法,即便同一個中間件類中的process_request()和process_view()方法會由於前面的一箇中間件返回HttpResponse而被跳過,process_response()方法老是會被調用。特別是,這意味着你的process_response()方法不能依賴於process_request()方法中的設置。
最後,記住在響應階段中,中間件以相反的順序被應用,自底向上。意思是定義在MIDDLEWARE_CLASSES最底下的類會最早被運行。
不像HttpResponse,StreamingHttpResponse並無content屬性。因此,中間件不再能假設全部響應都帶有content屬性。若是它們須要訪問內容,他們必須測試是否爲流式響應,並相應地調整本身的行爲。
if response.streaming: response.streaming_content = wrap_streaming_content(response.streaming_content) else: response.content = alter_content(response.content)
注意
咱們須要假設streaming_content可能會大到在內存中沒法容納。響應中間件可能會把它封裝在新的生成器中,可是必定不要銷燬它。封裝通常會實現成這樣:
def wrap_streaming_content(content): for chunk in content: yield alter_content(chunk)
request是一個HttpRequest對象。exception是一個被視圖中的方法拋出來的 Exception對象。
當一個視圖拋出異常時,Django會調用process_exception()來處理。process_exception()應該返回一個None或者一個HttpResponse對象。若是它返回一個HttpResponse對象,模型響應和響應中間件會被應用,響應結果會返回給瀏覽器。不然, 默認的異常處理機制將會被觸發。
再次提醒,在處理響應期間,中間件的執行順序是倒序執行的,這包括process_exception。若是一個異常處理的中間件返回了一個響應,那這個中間件上面的中間件都將不會被調用。
大多數的中間件類都不須要一個初始化方法,由於中間件的類定義僅僅是爲process_*提供一個佔位符。若是你確實須要一個全局的狀態那就能夠經過__init__來加載。而後要銘記以下兩個警告: Django初始化你的中間件無需任何參數,所以不要定義一個有參數的__init__方法。 不像process_* 對每一個請求都要調用,__init__ 只會被調用一次,就是在Web 服務器響應第一個請求的時候。 標記中間件不被使用¶ 有時在運行時決定是否一箇中間件須要被加載是頗有用的。 在這種狀況下,你的中間件中的 __init__方法能夠拋出一個django.core.exceptions.MiddlewareNotUsed異常。Django會從中間件處理過程當中移除這部分中間件,而且當DEBUG爲True的時候在django.request記錄器中記錄調試信息。
from django.shortcuts import render,HttpResponse #項目 abcd 文件名 abcd/middleware.py class BlockedIpMiddleware(object): def process_request(self, request): if request.META['REMOTE_ADDR'] in getattr(settings, "BLOCKED_IPS", []): return HttpResponse('<h1>Forbbiden</h1>') #----settings.py MIDDLEWARE_CLASSES = ( 'abcd.middleware.BlockedIpMiddleware', #...其它的中間件 )