在有些場合,須要對Django處理的每一個request都執行某段代碼。 這類代碼多是在view處理以前修改傳入的request,或者記錄日誌信息以便於調試,等等。正則表達式
這類功能能夠用Django的中間件框架來實現,該框架由切入到Django的request/response處理過程當中的鉤子集合組成。 這個輕量級低層次的plug-in系統,能用於全面的修改Django的輸入和輸出。算法
每一箇中間件組件都用於某個特定的功能。 若是你是順着這本書讀下來的話,你應該已經屢次見到「中間件」了數據庫
第12章中全部的session和user工具都籍由一小簇中間件實現(例如,由中間件設定view中可見的 request.session
和 request.user
)。django
第13章討論的站點範圍cache實際上也是由一箇中間件實現,一旦該中間件發現與view相應的response已在緩存中,就再也不調用對應的view函數。瀏覽器
第14章所介紹的 flatpages
, redirects
, 和 csrf
等應用也都是經過中間件組件來完成其魔法般的功能。緩存
這一章將深刻到中間件及其工做機制中,並闡述如何自行編寫中間件。服務器
咱們從一個簡單的例子開始。session
高流量的站點一般須要將Django部署在負載平衡proxy(參見第20章)以後。 這種方式將帶來一些複雜性,其一就是每一個request中的遠程IP地址(request.META["REMOTE_IP"]
)將指向該負載平衡proxy,而不是發起這個request的實際IP。 負載平衡proxy處理這個問題的方法在特殊的 X-Forwarded-For
中設置實際發起請求的IP。app
所以,須要一個小小的中間件來確保運行在proxy以後的站點也可以在 request.META["REMOTE_ADDR"]
中獲得正確的IP地址:框架
class SetRemoteAddrFromForwardedFor(object): def process_request(self, request): try: real_ip = request.META['HTTP_X_FORWARDED_FOR'] except KeyError: pass else: # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs. # Take just the first one. real_ip = real_ip.split(",")[0] request.META['REMOTE_ADDR'] = real_ip
(Note: Although the HTTP header is called X-Forwarded-For
, Django makes it available as request.META['HTTP_X_FORWARDED_FOR']
. With the exception of content-length
and content-type
, any HTTP headers in the request are converted to request.META
keys by converting all characters to uppercase, replacing any hyphens with underscores and adding an HTTP_
prefix to the name.)
一旦安裝了該中間件(參見下一節),每一個request中的 X-Forwarded-For
值都會被自動插入到 request.META['REMOTE_ADDR']
中。這樣,Django應用就不須要關心本身是否位於負載平衡proxy以後;簡單讀取 request.META['REMOTE_ADDR']
的方式在是否有proxy的情形下都將正常工做。
實際上,爲針對這個很是常見的情形,Django已將該中間件內置。 它位於 django.middleware.http
中, 下一節將給出這個中間件相關的更多細節。
若是按順序閱讀本書,應當已經看到涉及到中間件安裝的多個示例,由於前面章節的許多例子都須要某些特定的中間件。 出於完整性考慮,下面介紹如何安裝中間件。
要啓用一箇中間件,只需將其添加到配置模塊的 MIDDLEWARE_CLASSES
元組中。 在 MIDDLEWARE_CLASSES
中,中間件組件用字符串表示: 指向中間件類名的完整Python路徑。 例如,下面是 django-admin.py startproject
建立的缺省 MIDDLEWARE_CLASSES
:
MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', )
Django項目的安裝並不強制要求任何中間件,若是你願意, MIDDLEWARE_CLASSES
能夠爲空。
這裏中間件出現的順序很是重要。 在request和view的處理階段,Django按照 MIDDLEWARE_CLASSES
中出現的順序來應用中間件,而在response和異常處理階段,Django則按逆序來調用它們。 也就是說,Django將 MIDDLEWARE_CLASSES
視爲view函數外層的順序包裝子: 在request階段按順序從上到下穿過,而在response則反過來。
如今,咱們已經知道什麼是中間件和怎麼安裝它,下面將介紹中間件類中能夠定義的全部方法。
在中間件類中, __init__()
方法用於執行系統範圍的設置。
出於性能的考慮,每一個已啓用的中間件在每一個服務器進程中只初始化 一 次。 也就是說 __init__()
僅在服務進程啓動的時候調用,而在針對單個request處理時並不執行。
對一個middleware而言,定義 __init__()
方法的一般緣由是檢查自身的必要性。 若是 __init__()
拋出異常 django.core.exceptions.MiddlewareNotUsed
,則Django將從middleware棧中移出該middleware。 能夠用這個機制來檢查middleware依賴的軟件是否存在、服務是否運行於調試模式、以及任何其它環境因素。
在中間件中定義 __init__()
方法時,除了標準的 self
參數以外,不該定義任何其它參數。
這個方法的調用時機在Django接收到request以後,但仍未解析URL以肯定應當運行的view以前。 Django向它傳入相應的 HttpRequest
對象,以便在方法中修改。
process_request()
應當返回 None
或 HttpResponse
對象.
若是返回 None
, Django將繼續處理這個request,執行後續的中間件, 而後調用相應的view.
若是返回 HttpResponse
對象, Django 將再也不執行 任何 其它的中間件(而無視其種類)以及相應的view。 Django將當即返回該 HttpResponse
.
這個方法的調用時機在Django執行完request預處理函數並肯定待執行的view以後,但在view函數實際執行以前。
表15-1列出了傳入到這個View預處理函數的參數。
參數 | 說明 |
---|---|
request |
The HttpRequest object. |
view |
The Python function that Django will call to handle this request. This is the actual function object itself, not the name of the function as a string. |
args |
將傳入view的位置參數列表,但不包括
|
kwargs |
將傳入view的關鍵字參數字典. |
Just like process_request()
, process_view()
should return either None
or an HttpResponse
object.
If it returns None
, Django will continue processing this request, executing any other middleware and then the appropriate view.
If it returns an HttpResponse
object, Django won’t bother calling any other middleware (of any type) or the appropriate view. Django will immediately return that HttpResponse
.
這個方法的調用時機在Django執行view函數並生成response以後。 Here, the processor can modify the content of a response. One obvious use case is content compression, such as gzipping of the request’s HTML.
這個方法的參數至關直觀: request
是request對象,而 response
則是從view中返回的response對象。 request
is the request object, and response
is the response object returned from the view.
不一樣可能返回 None
的request和view預處理函數, process_response()
必須 返回 HttpResponse
對象. 這個response對象能夠是傳入函數的那一個原始對象(一般已被修改),也能夠是全新生成的。 That response could be the original one passed into the function (possibly modified) or a brand-new one.
這個方法只有在request處理過程當中出了問題而且view函數拋出了一個未捕獲的異常時纔會被調用。 這個鉤子能夠用來發送錯誤通知,將現場相關信息輸出到日誌文件, 或者甚至嘗試從錯誤中自動恢復。
這個函數的參數除了一向的 request
對象以外,還包括view函數拋出的實際的異常對象 exception
。
process_exception()
應當返回 None 或 HttpResponse 對象.
若是返回 None
, Django將用框架內置的異常處理機制繼續處理相應request。
若是返回 HttpResponse
對象, Django 將使用該response對象,而短路框架內置的異常處理機制。
備註
Django自帶了至關數量的中間件類(將在隨後章節介紹),它們都是至關好的範例。 閱讀這些代碼將使你對中間件的強大有一個很好的認識。
在Djangos wiki上也能夠找到大量的社區貢獻的中間件範例: http://code.djangoproject.com/wiki/ContributedMiddlewarehttp://code.djangoproject.com/wiki/ContributedMiddleware
Django自帶若干內置中間件以處理常見問題,將從下一節開始討論。
中間件類: django.contrib.auth.middleware.AuthenticationMiddleware
. django.contrib.auth.middleware.AuthenticationMiddleware
.
這個中間件激活認證支持功能. 它在每一個傳入的 HttpRequest
對象中添加表明當前登陸用戶的 request.user
屬性。 It adds the request.user
attribute, representing the currently logged-in user, to every incoming HttpRequest
object.
完整的細節請參見第12章。
Middleware class: django.middleware.common.CommonMiddleware
.
這個中間件爲完美主義者提供了一些便利:
禁止 ``DISALLOWED_USER_AGENTS`` 列表中所設置的user agent訪問 :一旦提供,這一列表應當由已編譯的正則表達式對象組成,這些對象用於匹配傳入的request請求頭中的user-agent域。 下面這個例子來自某個配置文件片斷:
import re DISALLOWED_USER_AGENTS = ( re.compile(r'^OmniExplorer_Bot'), re.compile(r'^Googlebot') )
請注意
import re
,由於DISALLOWED_USER_AGENTS
要求其值爲已編譯的正則表達式(也就是re.compile()
的返回值)。依據 ``APPEND_SLASH`` 和 ``PREPEND_WWW`` 的設置執行URL重寫 :若是
APPEND_SLASH
爲True
, 那些尾部沒有斜槓的URL將被重定向到添加了斜槓的相應URL,除非path的最末組成部分包含點號。 所以,foo.com/bar
會被重定向到foo.com/bar/
, 可是foo.com/bar/file.txt
將以不變形式經過。若是
PREPEND_WWW
爲 True , 那些缺乏先導www.的URLs將會被重定向到含有先導www.的相應URL上。 will be redirected to the same URL with a leading www..這兩個選項都是爲了規範化URL。 其後的哲學是每一個URL都應且只應當存在於一處。 技術上來講,URL
example.com/bar
與example.com/bar/
及www.example.com/bar/
都互不相同。依據 ``USE_ETAGS`` 的設置處理Etag : ETags 是HTTP級別上按條件緩存頁面的優化機制。 若是
USE_ETAGS
爲True
,Django針對每一個請求以MD5算法處理頁面內容,從而獲得Etag, 在此基礎上,Django將在適當情形下處理並返回Not Modified
迴應(譯註:請注意,還有一個條件化的
GET
中間件, 處理Etags並幹得更多,下面立刻就會說起。
中間件類 django.middleware.gzip.GZipMiddleware
.
這個中間件自動爲能處理gzip壓縮(包括全部的現代瀏覽器)的瀏覽器自動壓縮返回]內容。 這將極大地減小Web服務器所耗用的帶寬。 代價是壓縮頁面須要一些額外的處理時間。
相對於帶寬,人們通常更青睞於速度,可是若是你的情形正好相反,儘可啓用這個中間件。
Middleware class: django.middleware.http.ConditionalGetMiddleware
.
這個中間件對條件化 GET
操做提供支持。 若是response頭中包括 Last-Modified
或 ETag
域,而且request頭中包含 If-None-Match
或 If-Modified-Since
域,且二者一致,則該response將被response 304(Not modified)取代。 對 ETag
的支持依賴於 USE_ETAGS
配置及事先在response頭中設置 ETag
域。稍前所討論的通用中間件可用於設置response中的 ETag
域。 As discussed above, the ETag
header is set by the Common middleware.
此外,它也將刪除處理 HEAD
request時所生成的response中的任何內容,並在全部request的response頭中設置 Date
和 Content-Length
域。
Middleware class: django.middleware.http.SetRemoteAddrFromForwardedFor
.
這是咱們在 什麼是中間件 這一節中所舉的例子。 在 request.META['HTTP_X_FORWARDED_FOR']
存在的前提下,它根據其值來設置 request.META['REMOTE_ADDR']
。在站點位於某個反向代理以後的、每一個request的 REMOTE_ADDR
都被指向 127.0.0.1
的情形下,這一功能將很是有用。 It sets request.META['REMOTE_ADDR']
based on request.META['HTTP_X_FORWARDED_FOR']
, if the latter is set. This is useful if you’re sitting behind a reverse proxy that causes each request’s REMOTE_ADDR
to be set to 127.0.0.1
.
紅色警告!
這個middleware並 不 驗證 HTTP_X_FORWARDED_FOR
的合法性。
若是站點並不位於自動設置 HTTP_X_FORWARDED_FOR
的反向代理以後,請不要使用這個中間件。 不然,由於任何人都可以僞造 HTTP_X_FORWARDED_FOR
值,而 REMOTE_ADDR
又是依據 HTTP_X_FORWARDED_FOR
來設置,這就意味着任何人都可以僞造IP地址。
只有當可以絕對信任 HTTP_X_FORWARDED_FOR
值得時候纔可以使用這個中間件。
Middleware class: django.contrib.sessions.middleware.SessionMiddleware
.
這個中間件激活會話支持功能. 細節請參見第12章。 See Chapter 14 for details.
Middleware classes: django.middleware.cache.UpdateCacheMiddleware
and django.middleware.cache.FetchFromCacheMiddleware
.
這些中間件互相配合以緩存每一個基於Django的頁面。 已在第13章中詳細討論。
Middleware class: django.middleware.transaction.TransactionMiddleware
.
這個中間件將數據庫的 COMMIT
或 ROLLBACK
綁定到request/response處理階段。 若是view函數成功執行,則發出 COMMIT
指令。 若是view函數拋出異常,則發出 ROLLBACK
指令。
這個中間件在棧中的順序很是重要。 其外層的中間件模塊運行在Django缺省的 保存-提交 行爲模式下。 而其內層中間件(在棧中的其後位置出現)將置於與view函數一致的事務機制的控制下。
關於數據庫事務處理的更多信息,請參見附錄C。