按需内容处理

HTTP客户端可能发送一些协议头来告诉服务端它们已经看过了哪些资源。这在获取网页(使用HTTPGET请求)时非常常见,可以避免发送客户端已经获得的完整数据。然而,相同的协议头可用于所有HTTP方法(POST, PUT, DELETE, 以及其它)。

对于每一个Django从视图发回的页面(响应),都会提供两个HTTP协议头:ETagLast-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 modelsclass 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.etagdjango.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

如果你想要测试两个先决条件,把etaglast_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装饰器不仅仅对GETHEAD请求有用(HEAD请求在这种情况下和GET相同)。它也可以用于为 POST, PUTDELETE请求提供检查。在这些情况下,不是要返回一个“未修改(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.ConditionalGetMiddlewareCommonMiddleware.提供了简单和直接的GET 的按需处理。这些中间件易于使用并且适用于多种情况,然而它们的功能有一些高级用法上的限制:

  • 它们在全局上用于你项目中的所有视图。
  • 它们不会代替你生成响应本身,这可能要花一些代价。
  • 它们只适用于HTTP GET 请求。

在这里,你应该选择最适用于你特定问题的工具。如果你有办法快速计算出ETag和修改时间,并且如果一些视图需要花一些时间来生成内容,你应该考虑使用这篇文档描述的condition装饰器。如果一些都执行得非常快,坚持使用中间件在如果视图没有修改的条件下也会使发回客户端的网络流量也会减少。

译者:Django 文档协作翻译小组,原文:Conditional content processing。

本文以 CC BY-NC-SA 3.0 协议发布,转载请保留作者署名和文章出处。

Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。

django 1.8 官方文档翻译:14-1 按需内容处理相关推荐

  1. django 1.8 官方文档翻译: 2-5-7 自定义查找

    自定义查找 New in Django 1.7. Django为过滤提供了大量的内建的查找(例如,exact和icontains).这篇文档阐述了如何编写自定义查找,以及如何修改现存查找的功能.关于查 ...

  2. django 1.8 官方文档翻译: 1-2-2 编写你的第一个Django应用,第2部分

    编写你的第一个 Django 程序 第2部分 本教程上接 教程 第1部分 . 我们将继续开发 Web-poll 应用,并且专注在 Django 的 自动生成的管理网站上. 哲理 为你的员工或客户生成添 ...

  3. django 1.8 官方文档翻译: 2-6-3 提供初始数据

    Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质. 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html ...

  4. django 1.8 官方文档翻译: 13-9-1 如何使用会话

    如何使用会话 Django 提供对匿名会话的完全支持.其会话框架让你根据各个站点的访问者存储和访问任意数据.它在服务器端存储数据并抽象Cookie 的发送和接收.Cookie 包含会话的ID -- 不 ...

  5. django 1.8 官方文档翻译: 2-5-10 数据库函数

    数据库函数 New in Django 1.8. 下面记述的类为用户提供了一些方法,来在Django中使用底层数据库提供的函数用于注解.聚合或者过滤器等操作.函数也是表达式,所以可以像聚合函数一样混合 ...

  6. django 1.8 官方文档翻译: 3-3-5 编写自定义存储系统

    编写自定义存储系统 如果你需要提供自定义文件存储 – 一个普遍的例子是在某个远程系统上储存文件 – 你可以通过定义一个自定义的储存类来实现.你需要遵循以下步骤: 1. 你的自定义储存类必须是djang ...

  7. django 1.8 官方文档翻译: 3-3-3 文件储存API

    文件储存API 获取当前的储存类 Django提供了两个便捷的方法来获取当前的储存类: class DefaultStorage[source] DefaultStorage 提供对当前的默认储存系统 ...

  8. django 1.8 官方文档翻译: 2-5-6 多数据库

    多数据库 这篇主题描述Django 对多个数据库的支持.大部分Django 文档假设你只和一个数据库打交道.如果你想与多个数据库打交道,你将需要一些额外的步骤. 定义你的数据库 在Django中使用多 ...

  9. django 1.8 官方文档翻译: 1-1-2 快速安装指南

    快速安装指南 在你开始使用 Django 之前,你需要先安装它.我们有一个 完整安装指南 它涵盖了所有的安装步骤和可能遇到的问题:本指南将会给你一个最简单.简洁的安装指引. 安装 Python 作为一 ...

最新文章

  1. Ali RocketMQ与Kafka对照
  2. C++ STL库的总结以及实现原理
  3. 有了螃蟹让心情好一点
  4. geek_愚蠢的怪胎技巧:在Windows 7中启用秘密的“ How-To Geek”模式
  5. angular 数字逗号分隔_angular 实现的输入框数字千分位及保留几位小数点功能示例...
  6. Spring基于注解的方式一
  7. 提升开发效率的 Chrome 开发者工具快捷键参考
  8. 直播预告丨MySQL中的索引探究
  9. Android:Day03_完美登录案例(使用流和文件来保存登录信息)
  10. python和java的区别-python 和 java 的区别
  11. InDesign 软件教程,如何在 InDesign 中缩放和平移文档?
  12. 关于土地分类格式互转、土地利用转移矩阵、变化图谱计算详解
  13. Arduino开发遥控小车(二)基于nRF24L01无线模块实现数据发送和接收
  14. c++串口配置及DCB结构体
  15. linux绝育玩客云_绝育老母鸡(玩客云)pt下载浅谈
  16. C语言/C++编程学习:栈的代码实现之数组方案
  17. apktool解包和打包apk
  18. BUUCTF | [INSHack2017]sanity | [INSHack2019]INSAnity | [INSHack2019]Sanity | [INSHack2017]insanity-
  19. 构建基于Jenkins + Github的持续集成环境
  20. dw如何制作图片自动切换效果_如何在Dreamweaver中制作图片切换的效果?

热门文章

  1. (5)呼吸灯systemverilog与VHDL编码
  2. 2021-07-25 野火板子25MHz晶振改为正点原子8MH晶振
  3. java 递归_两篇文章带你了解java基础算法之递归和折半查找
  4. 新增成功到编制为空bug_36 个JS 面试题为你助力金九银10
  5. 栈和队列之栈的定义和实现
  6. Linux内核分析 - 网络[十二]:UDP模块 - 收发
  7. html是一种用于创建网页的标准标记语言,html
  8. java poi打印word_Java 打印Word文档
  9. 菜鸟学习笔记:Java提升篇6(IO流2——数据类型处理流、打印流、随机流)
  10. sql oracle 递归查询语句,深入sql oracle递归查询