[譯]django middleware介紹


Middleware

Middleware是一個鑲嵌到django的request/response處理機制中的一個hooks框架。它是一個修改django全局輸入輸出的一個底層插件系統。

每個中間件組件負責一些特定的功能,比如說 Django包含一個 AuthenticationMiddleware的中間間,使用sessions將用戶和請求聯系在一起( that associates users with requests using sessions.)。

這篇文章解釋了中間件如何工作,如果激活中間件,如何編寫自己的中間件。django有很多內建的中間件,詳細內容查看內建中間件附錄。

激活中間件

要想激活中間件,需要在settings文件中的 MIDDLEWARE_CLASSES下增加。在 MIDDLEWARE_CLASSES中,每個中間件以一個字符串的形勢保存。比如,下面是默認創建工程的中間件 MIDDLEWARE_CLASSES內容:

MIDDLEWARE_CLASSES = ['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',
]

一個django工程的創建不需要任何一個中間件,即如果你喜歡的話,MIDDLEWARE_CLASSES可以為空,但是強烈建議至少包含 CommonMiddleware這個中間件。
中間件在MIDDLEWARE_CLASSES中是有順序的,因為中間件之間有相互依賴關系。比如說AuthenticationMiddleware 在session中保存認證的用戶信息,因此,它必須在 SessionMiddleware之后,詳細的內容可以參考Middleware ordering

Hooks和application的順序

在請求階段,調用views之前,django按照MIDDLEWARE_CLASSES中定義的順序從上到下調用中間件。有兩個hooks:

  • process_request()
  • process_view()

在響應階段,調用views之后,中間件被從下到上反向調用,有三個hooks:

  • process_exception() (只有當view中raise一個例外時)
  • process_template_response() (只有view中返回template時)
  • process_response()

如果你喜歡,可以把它比作洋蔥,每個中間件就是洋蔥的一層皮。
每個hooks在下面描述:

編寫你自己的中間件

寫一個自己的中間件很簡單,每個中間件就是一個普通的python類,包含下面的一個或多個方法:

1) process_request()

process_request(request)
其中request是HttpRequest對象,process_request() 將會在每個request被決定使用哪個view之前調用,它會返回None或者一個HttpResponse對象。

  • 如果返回None,django會繼續執行其他中間件的process_request(), 然后是process_view() ,然后是恰當的view函數。
  • 如果返回HttpResponse對象,它就直接返回了,不再執行其他中間件,view等。

2) process_view()

process_view(request, view_func, view_args, view_kwargs)
其中request是HttpRequest 對象, view_func是django要使用的view函數 (它是實際的函數對象,而不是函數名字的字符串) view_args是view函數的列表參數, view_kwargs是view函數的字典參數。 view_args和 view_kwargs都不需要包含request這個參數。
process_view()就在django調用view函數之前被調用。
與process_request()相同,process_request()會返回None或者一個HttpResponse對象。

  • 如果返回None,django會繼續執行其他中間件的process_view(),然后是恰當的view函數。
  • 如果返回HttpResponse對象,它就直接返回了,不再執行其他中間件,view等。

Note
盡量避免使用process_request 或者process_view 來訪問request.POST。但是 CsrfViewMiddleware中間件是一個例外,它提供 csrf_exempt() 和 csrf_protect() 兩個裝飾器來解決此問題。
Accessing request.POST inside middleware from process_request orprocess_view will prevent any view running after the middleware from being able to modify the upload handlers for the request, and should normally be avoided.
The CsrfViewMiddleware class can be considered an exception, as it provides the csrf_exempt() and csrf_protect() decorators which allow views to explicitly control at what point the CSRF validation should occur.

3) process_template_response()

process_template_response(request, response)
其中request 是 HttpRequest 對象, response 是一個由django view或者中間件返回的TemplateResponse 對象。
process_template_response()在view使用render渲染一個模版對象完成之后被調用,它必須返回一個render 方法執行后的response對象,它可以修改view中返回的 response.template_name 和 response.context_data,或者為view返回的模板增加一個商標等等。
你不需要明確的渲染響應,當所有的template響應中間件處理完成后會被自動渲染。
帶有process_template_response()的中間件將會被自下而上反向執行。

4) process_response()

process_response(request, response)
其中request是 HttpRequest 對象, response 是一個django view或者中間件返回的 HttpResponse 或者StreamingHttpResponse對象。
process_response()在所有的響應被返回到瀏覽器之前執行。
必須返回一個 HttpResponse 或者StreamingHttpResponse 對象,它可以修改response, 或者為 HttpResponse 或StreamingHttpResponse增加一個商標等。
它不像 process_request() 和process_view() 方法可能會被跳過(在他之前有人返回了HttpResponse對象), process_response()方法一定會被執行。因此,你的process_response() 方法不能依賴於你的process_request()方法。
最后,記住在響應階段,中間件會被反向執行,你最后在 MIDDLEWARE_CLASSES定義的中間件將會被最先執行。

處理流式響應

不同於HttpResponse,StreamingHttpResponse沒有content屬性,因此中間件不能認為所有的響應都有content 屬性,如果想要訪問content,需要測試流式響應:

if response.streaming:
    response.streaming_content = wrap_streaming_content(response.streaming_content)
else:
    response.content = alter_content(response.content)

Note
streaming_content 被假定為太大而不能存放在內存中, 響應中間件可以將它包裹在一個生成器中,但是必須不能消費它,通常如下所示:

def wrap_streaming_content(content):
    for chunk in content:
        yield alter_content(chunk)

5) process_exception()

process_exception(request, exception)
其中request 是 HttpRequest 對象. exception是view函數中raise的Exception對象。
Django當view函數raise一個例外時調用process_exception() 。process_exception()返回None 或者HttpResponse 對象

  • 如果返回 HttpResponse 對象,template response 和response中間件會被應用, 然后在瀏覽器中返回結果,否則,默認的例外處理將會被使用。
    再說一遍,包含process_exception的中間件將會被反向執行,如果下面的中間件返回一個response,上面的中間間將不會被執行。

6) init()

大多數處理 process_* 方法的中間件都不需要自定義構造函數,如果你確實需要有一些全局的內容需要定義也可以使用,但是注意下面兩點:

  • Django初始化中間件不會包含任何參數,所以不能在__init__ 要求任何參數;
  • 不像process_* 方法在每次請求時調用,__init__方法只會在web server首次處理請求的時候調用。

標記中間件為不可用

有時候在運行的時候決定哪個中間件是否可用是十分必要的,這種情況下你的自定義中間件__init__方法可以 raise django.core.exceptions.MiddlewareNotUsed,django將會移除中間件,並且報一個日志,如果DEBUG打開的話,可以在django.request logger里看到。(1.8之前的版本,MiddlewareNotUsed不會在日志中體現)

7)指導意見

  • 中間件不繼承;
  • 中間件寫在哪里都行,只需要加到MIDDLEWARE_CLASSES settings中。

來源: https://docs.djangoproject.com/en/1.9/topics/http/middleware/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM