ETag 和 Last-Modified 能让浏览器少拉内容,但「少拉」的是带宽还是计算?Django 有两条路:中间件自动算,和 @condition 提前判断。这篇数视图被执行的次数,把两条路的差别直接量出来。
第 1 次: status 200 ETag = "164c93b1e8f663c2439805c7f8474f14" 体 5000 字节 视图调用次数 1 带 If-None-Match: 304 体 0 字节 视图调用次数 2 (视图还是执行了)
中间件在响应阶段根据内容算出 ETag,再和请求头比较。视图已经跑完、响应已经生成,所以只能省下传输,省不了计算。
第 1 次 GET /etag/: 200 "v1" 视图调用 1
If-None-Match: "v1" → 304 体 0 字节 视图调用 1 304 响应头: {'ETag': '"v1"'}
If-None-Match: "v2" → 200 视图调用 2
If-None-Match: W/"v1"(弱校验器) → 304
If-None-Match: * → 304etag_func / last_modified_func 在视图之前调用(应当是便宜的,比如读一个版本号或最后更新时间)。命中时直接返回 304,昂贵的视图不执行。
第 1 次: 200 Last-Modified = Thu, 01 Jan 2026 12:00:00 GMT 视图调用 1 If-Modified-Since: Thu, 01 Jan 2026 12:00:00 GMT → 304 If-Modified-Since: Thu, 01 Jan 2026 11:59:59 GMT → 200 If-Modified-Since: Thu, 01 Jan 2026 12:00:01 GMT → 304 If-Modified-Since: garbage → 200 视图累计调用 3
比较的是秒级时间。客户端传来不合法的日期时被当成没传。
If-None-Match "v1" If-Modified-Since 01 Jan 2026 12:00:00… → 304 If-None-Match "v2" If-Modified-Since 01 Jan 2026 12:00:00… → 200 If-None-Match "v1" If-Modified-Since 01 Jan 2026 11:00:00… → 304 POST 带 If-None-Match: "v1" → 412
同时提供两个函数时,两个条件都参与判断;实测 ETag 匹配即 304(哪怕 If-Modified-Since 更早)。注意对不安全方法(POST)带 If-None-Match 的处理是 412。