第十七章: 中間件

第十七章: 中間件

在有些場合,須要對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則反過來。

中間件方法

如今,咱們已經知道什麼是中間件和怎麼安裝它,下面將介紹中間件類中能夠定義的全部方法。

Initializer: __init__(self) __init__(self)「初始化]

在中間件類中, __init__() 方法用於執行系統範圍的設置。

出於性能的考慮,每一個已啓用的中間件在每一個服務器進程中只初始化  次。 也就是說 __init__() 僅在服務進程啓動的時候調用,而在針對單個request處理時並不執行。

對一個middleware而言,定義 __init__() 方法的一般緣由是檢查自身的必要性。 若是 __init__() 拋出異常 django.core.exceptions.MiddlewareNotUsed ,則Django將從middleware棧中移出該middleware。 能夠用這個機制來檢查middleware依賴的軟件是否存在、服務是否運行於調試模式、以及任何其它環境因素。

在中間件中定義 __init__() 方法時,除了標準的 self 參數以外,不該定義任何其它參數。

Request預處理函數: process_request(self, request) process_request(self, request)

這個方法的調用時機在Django接收到request以後,但仍未解析URL以肯定應當運行的view以前。 Django向它傳入相應的 HttpRequest 對象,以便在方法中修改。

process_request() 應當返回 None 或 HttpResponse 對象.

  • 若是返回 None , Django將繼續處理這個request,執行後續的中間件, 而後調用相應的view.

  • 若是返回 HttpResponse 對象, Django 將再也不執行 任何 其它的中間件(而無視其種類)以及相應的view。 Django將當即返回該 HttpResponse .

View預處理函數: process_view(self, request, view, args, kwargs) process_view(self, request, view, args, kwargs)

這個方法的調用時機在Django執行完request預處理函數並肯定待執行的view以後,但在view函數實際執行以前。

表15-1列出了傳入到這個View預處理函數的參數。

表 15-1. 傳入process_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的位置參數列表,但不包括

request 參數(它一般是傳 入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 .

Response後處理函數: process_response(self, request, response) process_response(self, request, response)

這個方法的調用時機在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.

Exception後處理函數: process_exception(self, request, exception) process_exception(self, request, exception)

這個方法只有在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服務器所耗用的帶寬。 代價是壓縮頁面須要一些額外的處理時間。

相對於帶寬,人們通常更青睞於速度,可是若是你的情形正好相反,儘可啓用這個中間件。

條件化的GET中間件

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 域。

反向代理支持 (X-Forwarded-For中間件)

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。

相關文章
相關標籤/搜索