Middleware

中間件是一個鉤子框架,是介於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能夠爲空 —— 可是強烈推薦你至少使用CommonMiddlewaresession

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)
View Code

編寫本身的中間件很容易的。每一箇中間件組件是一個獨立的Python 類,你能夠定義下面這些方法中的一個或多個: 

process_request

process_request(request)

request是一個HttpRequest 對象。

在Django決定執行哪一個視圖以前,process_request()會在每一個請求上調用。

它應該返回一個None 或一個HttpResponse對象。若是返回None,Django會繼續處理這個請求,執行其它process_request()中間件,而後process_view()中間件,最後是對應的視圖。若是它返回一個HttpResponse對象,Django 就不用再去調用其它的request、view 或exception 中間件,或對應的視圖;它將對HttpResponse 運用響應階段的中間件,並返回結果。

process_view

process_view(request, view_func, view_args, view_kwargs)

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 

process_template_response(request, response)

request是一個HttpRequest對象。response是一個TemplateResponse對象(或等價的對象),由Django視圖或者中間件返回。

若是響應的實例有render()方法,process_template_response()在視圖恰好執行完畢以後被調用,這代表了它是一個TemplateResponse對象(或等價的對象)。

這個方法必須返回一個實現了render方法的響應對象。它能夠修改給定的response對象,經過修改 response.template_nameresponse.context_data或者它能夠建立一個全新的 TemplateResponse或等價的對象。

你不須要顯式渲染響應 —— 一旦全部的模板響應中間件被調用,響應會自動被渲染。

在一個響應的處理期間,中間件以相反的順序運行,這包括process_template_response()

process_response

process_response(request, response)

request是一個HttpRequest對象。response是Django視圖或者中間件返回的HttpResponse或者StreamingHttpResponse對象。

process_response()在全部響應返回瀏覽器以前被調用。

這個方法必須返回HttpResponse或者StreamingHttpResponse對象。它能夠改變已有的response,或者建立並返回新的HttpResponseStreamingHttpResponse對象。

不像 process_request()process_view()方法,即便同一個中間件類中的process_request()process_view()方法會由於前面的一箇中間件返回HttpResponse而被跳過,process_response()方法老是會被調用。特別是,這意味着你的process_response()方法不能依賴於process_request()方法中的設置。

最後,記住在響應階段中,中間件以相反的順序被應用,自底向上。意思是定義在MIDDLEWARE_CLASSES最底下的類會最早被運行。

處理流式響應

不像HttpResponseStreamingHttpResponse並無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)
View Code

process_exception

process_exception(request, exception)

request是一個HttpRequest對象。exception是一個被視圖中的方法拋出來的 Exception對象。

當一個視圖拋出異常時,Django會調用process_exception()來處理。process_exception()應該返回一個None或者一個HttpResponse對象。若是它返回一個HttpResponse對象,模型響應和響應中間件會被應用,響應結果會返回給瀏覽器。不然, 默認的異常處理機制將會被觸發。

再次提醒,在處理響應期間,中間件的執行順序是倒序執行的,這包括process_exception若是一個異常處理的中間件返回了一個響應,那這個中間件上面的中間件都將不會被調用。

__init__  

大多數的中間件類都不須要一個初始化方法,由於中間件的類定義僅僅是爲process_*提供一個佔位符。若是你確實須要一個全局的狀態那就能夠經過__init__來加載。而後要銘記以下兩個警告:

Django初始化你的中間件無需任何參數,所以不要定義一個有參數的__init__方法。
不像process_* 對每一個請求都要調用,__init__ 只會被調用一次,就是在Web 服務器響應第一個請求的時候。
標記中間件不被使用¶
有時在運行時決定是否一箇中間件須要被加載是頗有用的。 在這種狀況下,你的中間件中的 __init__方法能夠拋出一個django.core.exceptions.MiddlewareNotUsed異常。Django會從中間件處理過程當中移除這部分中間件,而且當DEBUG爲True的時候在django.request記錄器中記錄調試信息。
View Code

應用

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',
    #...其它的中間件
)
攔截器
相關文章
相關標籤/搜索