天天看點

django 1.8 官方文檔翻譯:14-1 按需内容處理按需内容處理

按需内容處理

HTTP用戶端可能發送一些協定頭來告訴服務端它們已經看過了哪些資源。這在擷取網頁(使用HTTP

GET

請求)時非常常見,可以避免發送用戶端已經獲得的完整資料。然而,相同的協定頭可用于所有HTTP方法(

POST

,

PUT

DELETE

, 以及其它)。

對于每一個Django從視圖發回的頁面(響應),都會提供兩個HTTP協定頭:

ETag

Last-Modified

。這些協定頭在HTTP響應中是可選的。它們可以由你的視圖函數設定,或者你可以依靠 

CommonMiddleware

中間件來設定

ETag

協定頭。

當你的用戶端再次請求相同的資源時,它可能會發送 

If-modified-since

或者

If-unmodified-since

的協定頭,包含之前發送的最後修改時間;或者 

If-match

If-none-match

協定頭,包含之前發送的

ETag

。如果頁面的目前版本比對用戶端發送的

ETag

,或者如果資源沒有被修改,會發回304狀态碼,而不是一個完整的回複,告訴用戶端沒有任何修改。根據協定頭,如果頁面被修改了,或者不比對用戶端發送的 

ETag

,會傳回412(先決條件失敗,Precondition Failed)狀态碼。

當你需要更多精細化的控制時,你可以使用每個視圖的按需處理函數。

Changed in Django 1.8:

向按需視圖處理添加

If-unmodified-since

協定頭的支援

The

condition

有時(實際上是經常),你可以建立一些函數來快速計算出資源的

ETag

值或者最後修改時間,并不需要執行建構完整視圖所需的所有步驟。Django可以使用這些函數來為視圖處理提供一個“early bailout”的選項。來告訴用戶端,内容自從上次請求并沒有任何改動。

這兩個函數作為參數傳遞到

django.views.decorators.http.condition

裝飾器中。這個裝時期使用這兩個函數(如果你不能既快又容易得計算出來,你隻需要提供一個)來弄清楚是否HTTP請求中的協定頭比對那些資源。如果它們不比對,會生成資源的一份新的副本,并調用你的普通視圖。

condition

裝飾器的簽名為i:

condition(etag_func=None, last_modified_func=None)
           

計算ETag的最後修改時間的兩個函數,會以相同的順序傳入

request

對象和相同的參數,就像它們封裝的視圖函數那樣。

last_modified_func

函數應該傳回一個标準的datetime值,它制訂了資源修改的最後時間,或者資源不存在為 

None

。傳遞給

etag

裝飾器的函數應該傳回一個表示資源

Etag

的字元串,或者資源不存在時為

None

用一個例子可以很好展示如何使用這一特性。假設你有這兩個模型,表示一個簡單的部落格系統:

import datetime
from django.db import models

class Blog(models.Model):
    ...

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    published = models.DateTimeField(default=datetime.datetime.now)
    ...
           

如果頭版展示最後的部落格文章,僅僅在你添加新文章的時候修改,你可以非常快速地計算出最後修改時間。你需要這個部落格每一篇文章的最後 

釋出

日期。實作它的一種方式是:

def latest_entry(request, blog_id):
    return Entry.objects.filter(blog=blog_id).latest("published").published
           

接下來你可以使用這個函數,來為你的頭版視圖事先探測未修改的頁面:

from django.views.decorators.http import condition

@condition(last_modified_func=latest_entry)
def front_page(request, blog_id):
    ...
           

隻計算一個值的快捷方式

一個普遍的原則是,如果你提供了計算 ETag_和_最後修改時間的函數,你應該這樣做:你并不知道HTTP用戶端會發給你哪個協定頭,是以要準備好處理兩種情況。但是,有時隻有二者之一容易計算,并且Django隻提供給你計算ETag或最後修改日期的裝飾器。

django.views.decorators.http.etag

django.views.decorators.http.last_modified

作為

condition

裝飾器,傳入相同類型的函數。他們的簽名是:

etag(etag_func)
last_modified(last_modified_func)
           

我們可以編寫一個初期的示例,它僅僅使用最後修改日期的函數,使用這些裝飾器之一:

@last_modified(latest_entry)
def front_page(request, blog_id):
    ...
           

…或者:

def front_page(request, blog_id):
    ...
front_page = last_modified(latest_entry)(front_page)
           

Use

condition

如果你想要測試兩個先決條件,把

etag

last_modified

裝飾器鍊到一起看起來很不錯。但是,這會導緻不正确的行為:

# Bad code. Don't do this!
@etag(etag_func)
@last_modified(last_modified_func)
def my_view(request):
    # ...

# End of bad code.
           

第一個裝飾器不知道後面的任何事情,并且可能發送“未修改”的響應,即使第二個裝飾器會處理别的事情。

condition

裝飾器同時更使用兩個回調函數,來弄清楚哪個是正确的行為。

使用帶有其它HTTP方法的裝飾器

condition

裝飾器不僅僅對

GET

HEAD

請求有用(

HEAD

請求在這種情況下和

GET

相同)。它也可以用于為

POST

PUT

DELETE

請求提供檢查。在這些情況下,不是要傳回一個“未修改(not modified,314)”的響應,而是要告訴服務端,它們嘗試修改的資源在此期間被修改了。

例如,考慮以下用戶端和服務端之間的互動:

  1. 用戶端請求

    /foo/

  2. 服務端回複一些帶有

    "abcd1234"

    ETag的内容。
  3. 用戶端發送HTTP

    PUT

    請求到

    /foo/

    來更新資源。同時也發送了

    If-Match: "abcd1234"

    協定頭來指定嘗試更新的版本。
  4. 服務端檢查是否資源已經被修改,通過和

    GET

    上所做的相同方式計算ETag(使用相同的函數)。如果資源 已經 修改了,會傳回412狀态碼,意思是“先決條件失敗(precondition failed)”。
  5. 用戶端在接收到412響應之後,發送

    GET

    /foo/

    ,來在更新之前擷取内容的新版本。

重要的事情是,這個例子展示了在所有情況下,ETag和最後修改時間值都采用相同函數計算。實際上,你 應該 使用相同函數,以便每次都傳回相同的值。

使用中間件按需處理來比較

你可能注意到,Django已經通過

django.middleware.http.ConditionalGetMiddleware

CommonMiddleware

.提供了簡單和直接的

GET

的按需處理。這些中間件易于使用并且适用于多種情況,然而它們的功能有一些進階用法上的限制:

  • 它們在全局上用于你項目中的所有視圖。
  • 它們不會代替你生成響應本身,這可能要花一些代價。
  • 它們隻适用于HTTP

    GET

    請求。

在這裡,你應該選擇最适用于你特定問題的工具。如果你有辦法快速計算出ETag和修改時間,并且如果一些視圖需要花一些時間來生成内容,你應該考慮使用這篇文檔描述的

condition

裝飾器。如果一些都執行得非常快,堅持使用中間件在如果視圖沒有修改的條件下也會使發回用戶端的網絡流量也會減少。

譯者: Django 文檔協作翻譯小組 ,原文: Conditional content processing 本文以 CC BY-NC-SA 3.0 協定釋出,轉載請保留作者署名和文章出處。 人手緊缺,有興趣的朋友可以加入我們,完全公益性質。交流群:467338606。