鍍金池/ 教程/ Python/ 按需內(nèi)容處理
點(diǎn)擊劫持保護(hù)
安全問(wèn)題歸檔
Model 類(lèi)參考
將遺留數(shù)據(jù)庫(kù)整合到Django
關(guān)聯(lián)對(duì)象參考
內(nèi)建基于類(lèi)的視圖的API
聚合
Django 中的用戶(hù)認(rèn)證
django.contrib.humanize
Django管理文檔生成器
分頁(yè)
使用Django輸出CSV
加密簽名
文件儲(chǔ)存API
安全
Django中的測(cè)試
國(guó)際化和本地化
為Django編寫(xiě)首個(gè)補(bǔ)丁
條件表達(dá)式
日志
模型元選項(xiàng)
部署靜態(tài)文件
執(zhí)行查詢(xún)
使用Django認(rèn)證系統(tǒng)
基于類(lèi)的視圖
中間件
編寫(xiě)自定義的django-admin命令
Django 的設(shè)置
格式本地化
數(shù)據(jù)庫(kù)訪問(wèn)優(yōu)化
錯(cuò)誤報(bào)告
基于類(lèi)的內(nèi)建通用視圖
編寫(xiě)自定義存儲(chǔ)系統(tǒng)
編寫(xiě)你的第一個(gè) Django 程序 第3部分
編寫(xiě)數(shù)據(jù)庫(kù)遷移
使用表單
編寫(xiě)你的第一個(gè) Django 程序 第2部分
編寫(xiě)你的第一個(gè) Django 程序 第1部分
如何使用會(huì)話
系統(tǒng)檢查框架
新手入門(mén)
信號(hào)
編寫(xiě)視圖
如何使用WSGI 部署
編寫(xiě)你的第一個(gè)Django應(yīng)用,第6部分
常見(jiàn)的網(wǎng)站應(yīng)用工具
Widgets
內(nèi)建的視圖
模型實(shí)例參考
視圖層
Django中的密碼管理
高級(jí)教程:如何編寫(xiě)可重用的應(yīng)用
國(guó)際化和本地化
"本地特色"附加功能
TemplateResponse 和 SimpleTemplateResponse
模式編輯器
文件上傳
快速安裝指南
部署 Django
表單 API
表單素材 ( <code>Media</code> 類(lèi))
管理文件
其它核心功能
查找 API 參考
表單
Admin
數(shù)據(jù)庫(kù)函數(shù)
自定義查找
使用基于類(lèi)的視圖處理表單
管理操作
開(kāi)發(fā)過(guò)程
編寫(xiě)你的第一個(gè)Django應(yīng)用,第5部分
進(jìn)行原始的sql查詢(xún)
模型層
多數(shù)據(jù)庫(kù)
編寫(xiě)你的第一個(gè) Django 程序 第4部分
Django安全
Django 初探
Django異常
重定向應(yīng)用
按需內(nèi)容處理
管理器
視圖裝飾器
驗(yàn)證器
使用Django輸出PDF
File對(duì)象
Django 的快捷函數(shù)
基于類(lèi)的通用視圖 —— 索引
為模型提供初始數(shù)據(jù)
模板層
URL調(diào)度器
中間件
模型

按需內(nèi)容處理

HTTP客戶(hù)端可能發(fā)送一些協(xié)議頭來(lái)告訴服務(wù)端它們已經(jīng)看過(guò)了哪些資源。這在獲取網(wǎng)頁(yè)(使用HTTPGET請(qǐng)求)時(shí)非常常見(jiàn),可以避免發(fā)送客戶(hù)端已經(jīng)獲得的完整數(shù)據(jù)。然而,相同的協(xié)議頭可用于所有HTTP方法(POST, PUT, DELETE, 以及其它)。

對(duì)于每一個(gè)Django從視圖發(fā)回的頁(yè)面(響應(yīng)),都會(huì)提供兩個(gè)HTTP協(xié)議頭:ETagLast-Modified。這些協(xié)議頭在HTTP響應(yīng)中是可選的。它們可以由你的視圖函數(shù)設(shè)置,或者你可以依靠 CommonMiddleware 中間件來(lái)設(shè)置ETag 協(xié)議頭。

當(dāng)你的客戶(hù)端再次請(qǐng)求相同的資源時(shí),它可能會(huì)發(fā)送 If-modified-since 或者If-unmodified-since的協(xié)議頭,包含之前發(fā)送的最后修改時(shí)間;或者 If-matchIf-none-match協(xié)議頭,包含之前發(fā)送的ETag。如果頁(yè)面的當(dāng)前版本匹配客戶(hù)端發(fā)送的ETag,或者如果資源沒(méi)有被修改,會(huì)發(fā)回304狀態(tài)碼,而不是一個(gè)完整的回復(fù),告訴客戶(hù)端沒(méi)有任何修改。根據(jù)協(xié)議頭,如果頁(yè)面被修改了,或者不匹配客戶(hù)端發(fā)送的 ETag,會(huì)返回412(先決條件失敗,Precondition Failed)狀態(tài)碼。

當(dāng)你需要更多精細(xì)化的控制時(shí),你可以使用每個(gè)視圖的按需處理函數(shù)。

Changed in Django 1.8:

向按需視圖處理添加If-unmodified-since協(xié)議頭的支持

The condition

有時(shí)(實(shí)際上是經(jīng)常),你可以創(chuàng)建一些函數(shù)來(lái)快速計(jì)算出資源的ETag值或者最后修改時(shí)間,并不需要執(zhí)行構(gòu)建完整視圖所需的所有步驟。Django可以使用這些函數(shù)來(lái)為視圖處理提供一個(gè)“early bailout”的選項(xiàng)。來(lái)告訴客戶(hù)端,內(nèi)容自從上次請(qǐng)求并沒(méi)有任何改動(dòng)。

這兩個(gè)函數(shù)作為參數(shù)傳遞到django.views.decorators.http.condition裝飾器中。這個(gè)裝時(shí)期使用這兩個(gè)函數(shù)(如果你不能既快又容易得計(jì)算出來(lái),你只需要提供一個(gè))來(lái)弄清楚是否HTTP請(qǐng)求中的協(xié)議頭匹配那些資源。如果它們不匹配,會(huì)生成資源的一份新的副本,并調(diào)用你的普通視圖。

condition裝飾器的簽名為i:

condition(etag_func=None, last_modified_func=None)

計(jì)算ETag的最后修改時(shí)間的兩個(gè)函數(shù),會(huì)以相同的順序傳入request對(duì)象和相同的參數(shù),就像它們封裝的視圖函數(shù)那樣。last_modified_func函數(shù)應(yīng)該返回一個(gè)標(biāo)準(zhǔn)的datetime值,它制訂了資源修改的最后時(shí)間,或者資源不存在為 None。傳遞給etag裝飾器的函數(shù)應(yīng)該返回一個(gè)表示資源Etag的字符串,或者資源不存在時(shí)為None

用一個(gè)例子可以很好展示如何使用這一特性。假設(shè)你有這兩個(gè)模型,表示一個(gè)簡(jiǎn)單的博客系統(tǒng):

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)
    ...

如果頭版展示最后的博客文章,僅僅在你添加新文章的時(shí)候修改,你可以非??焖俚赜?jì)算出最后修改時(shí)間。你需要這個(gè)博客每一篇文章的最后 發(fā)布 日期。實(shí)現(xiàn)它的一種方式是:

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

接下來(lái)你可以使用這個(gè)函數(shù),來(lái)為你的頭版視圖事先探測(cè)未修改的頁(yè)面:

from django.views.decorators.http import condition

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

只計(jì)算一個(gè)值的快捷方式

一個(gè)普遍的原則是,如果你提供了計(jì)算 ETag_和_最后修改時(shí)間的函數(shù),你應(yīng)該這樣做:你并不知道HTTP客戶(hù)端會(huì)發(fā)給你哪個(gè)協(xié)議頭,所以要準(zhǔn)備好處理兩種情況。但是,有時(shí)只有二者之一容易計(jì)算,并且Django只提供給你計(jì)算ETag或最后修改日期的裝飾器。

django.views.decorators.http.etagdjango.views.decorators.http.last_modified作為condition裝飾器,傳入相同類(lèi)型的函數(shù)。他們的簽名是:

etag(etag_func)
last_modified(last_modified_func)

我們可以編寫(xiě)一個(gè)初期的示例,它僅僅使用最后修改日期的函數(shù),使用這些裝飾器之一:

@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

如果你想要測(cè)試兩個(gè)先決條件,把etaglast_modified裝飾器鏈到一起看起來(lái)很不錯(cuò)。但是,這會(huì)導(dǎo)致不正確的行為:

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

# End of bad code.

第一個(gè)裝飾器不知道后面的任何事情,并且可能發(fā)送“未修改”的響應(yīng),即使第二個(gè)裝飾器會(huì)處理別的事情。condition裝飾器同時(shí)更使用兩個(gè)回調(diào)函數(shù),來(lái)弄清楚哪個(gè)是正確的行為。

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

condition裝飾器不僅僅對(duì)GETHEAD請(qǐng)求有用(HEAD請(qǐng)求在這種情況下和GET相同)。它也可以用于為 POST, PUTDELETE請(qǐng)求提供檢查。在這些情況下,不是要返回一個(gè)“未修改(not modified,314)”的響應(yīng),而是要告訴服務(wù)端,它們嘗試修改的資源在此期間被修改了。

例如,考慮以下客戶(hù)端和服務(wù)端之間的交互:

  1. 客戶(hù)端請(qǐng)求/foo/。
  2. 服務(wù)端回復(fù)一些帶有"abcd1234"ETag的內(nèi)容。
  3. 客戶(hù)端發(fā)送HTTP PUT 請(qǐng)求到 /foo/ 來(lái)更新資源。同時(shí)也發(fā)送了If-Match: "abcd1234" 協(xié)議頭來(lái)指定嘗試更新的版本。
  4. 服務(wù)端檢查是否資源已經(jīng)被修改,通過(guò)和GET 上所做的相同方式計(jì)算ETag(使用相同的函數(shù))。如果資源 已經(jīng) 修改了,會(huì)返回412狀態(tài)碼,意思是“先決條件失?。╬recondition failed)”。
  5. 客戶(hù)端在接收到412響應(yīng)之后,發(fā)送 GET請(qǐng)求到 /foo/,來(lái)在更新之前獲取內(nèi)容的新版本。

重要的事情是,這個(gè)例子展示了在所有情況下,ETag和最后修改時(shí)間值都采用相同函數(shù)計(jì)算。實(shí)際上,你 應(yīng)該 使用相同函數(shù),以便每次都返回相同的值。

使用中間件按需處理來(lái)比較

你可能注意到,Django已經(jīng)通過(guò)django.middleware.http.ConditionalGetMiddlewareCommonMiddleware.提供了簡(jiǎn)單和直接的GET 的按需處理。這些中間件易于使用并且適用于多種情況,然而它們的功能有一些高級(jí)用法上的限制:

  • 它們?cè)谌稚嫌糜谀沩?xiàng)目中的所有視圖。
  • 它們不會(huì)代替你生成響應(yīng)本身,這可能要花一些代價(jià)。
  • 它們只適用于HTTP GET 請(qǐng)求。

在這里,你應(yīng)該選擇最適用于你特定問(wèn)題的工具。如果你有辦法快速計(jì)算出ETag和修改時(shí)間,并且如果一些視圖需要花一些時(shí)間來(lái)生成內(nèi)容,你應(yīng)該考慮使用這篇文檔描述的condition裝飾器。如果一些都執(zhí)行得非??欤瑘?jiān)持使用中間件在如果視圖沒(méi)有修改的條件下也會(huì)使發(fā)回客戶(hù)端的網(wǎng)絡(luò)流量也會(huì)減少。

譯者:Django 文檔協(xié)作翻譯小組,原文:Conditional content processing。

本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請(qǐng)保留作者署名和文章出處。

Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。