If-None-Match 是 If-Modified-Since 的精确版,凭证是 ETag。
两者同时出现时,它优先级更高——只要它出现,If-Modified-Since 就被完全忽略。
上一节的时间凭证有精度问题,这一节换成实体标签。两个一起给的时候听谁的,是这一节的核心。
一、ETag 比时间强在哪
ETag 是服务端自己算的一个字符串,可以是内容哈希,也可以只是个版本号,没有精度限制。
所以同一秒内改两次,时间戳看不出来,ETag 一定变。这一条就把它和 Last-Modified 彻底分开了。
代价是服务端要为每个资源算一个值。内容哈希最稳,但对大文件要算一遍;
用版本号最省,但得自己维护一致性。
二、优先级:INM 说了算
Demo 第四组是关键:If-None-Match 给了一个错的值,If-Modified-Since 给的却是对的。
如果服务端先看时间,会回 304;按规范必须先看 ETag,所以回了 200。
输出里那一行「只看 If-None-Match」就是判定依据。
这条规矩的意义:客户端同时给两个凭证,是想让服务端「能判准就用准的,判不准再退回时间」。
所以服务端必须优先用 ETag。
三、* 的另一种用法
If-None-Match: * 表示「只要资源存在,就别给我」。用在 GET 上,效果是「存在就回 304」;
用在 PUT 上,效果是「存在就回 412」。
所以它是「只创建,不覆盖」的写法:PUT 加 If-None-Match: *,资源已存在就失败,
不存在才创建。这比先 GET 再 PUT 安全,因为没有竞态窗口。
Demo 第五组用星号请求一个存在的资源,回了 304。
四、动手 Demo:五组条件,验证优先级
服务端同时支持两种凭证。 分别测:只有 INM 值对、只有 INM 值不对、只有 IMS 值对、两个都给但 INM 错、以及 INM 用星号。
if inm:
if inm == ETAG or inm == u'*':
st[u'used'] = u'只看 If-None-Match'
return reply(u'HTTP/1.1 304 Not Modified', [(u'ETag', ETAG)], b'')
st[u'used'] = u'只看 If-None-Match'
return reply(u'HTTP/1.1 200 OK', [(u'ETag', ETAG)], DOC)
if ims:
st[u'used'] = u'If-None-Match 缺,退回看 If-Modified-Since'
if ims == LM:
return reply(u'HTTP/1.1 304 Not Modified', ..., b'')
实跑输出:
--------------------------------------------------------------------
If-None-Match:优先级高于 If-Modified-Since
--------------------------------------------------------------------
【只有 INM,值对】
服务端判定依据 只看 If-None-Match
结果 HTTP/1.1 304 Not Modified | 正文 0 字节
【只有 INM,值不对】
服务端判定依据 只看 If-None-Match
结果 HTTP/1.1 200 OK | 正文 38 字节
【只有 IMS,值对】
服务端判定依据 If-None-Match 缺,退回看 If-Modified-Since
结果 HTTP/1.1 304 Not Modified | 正文 0 字节
【两个都给,INM 值不对但 IMS 值对】
服务端判定依据 只看 If-None-Match
结果 HTTP/1.1 200 OK | 正文 38 字节
【INM 用星号】
服务端判定依据 只看 If-None-Match
结果 HTTP/1.1 304 Not Modified | 正文 0 字节
第四组是关键:`If-None-Match` 给了个错的值,`If-Modified-Since` 给的却是对的。
如果服务端先看 IMS,就会回 304;按规范必须先看 INM,所以回了 200。
**只要 INM 出现,IMS 就被完全忽略**——这就是优先级。
为什么 INM 更强:`Last-Modified` 只有秒级精度,
同一秒内的两次修改它区分不出来;ETag 是服务端自己算的,可以精确到字节。
所以同时给两个字段时,说明这个服务端既想兼容老客户端,又想自己判准。
只有 INM 值对:304,正文 0 字节。 只有 INM 值不对:200,正文 38 字节。 只有 IMS 值对:304,判定那一行变成「If-None-Match 缺,退回看…」。 两个都给、INM 错而 IMS 对:200。这就是优先级的实证——服务端根本没看那个正确的时间。 INM 用星号:304。资源存在,所以「别给我」。
30 秒实验
- 把两个都给的那组改成 INM 对、IMS 错,看结果(应该是 304)。
- 用
PUT加If-None-Match: *试一次已存在的资源,看是不是 412。 - 想一想:为什么服务端要同时发 ETag 和 Last-Modified?只发一个不行吗?
常见误区
- 「两个都给时以时间更准的那个为准」——以 ETag 为准,与准不准无关。
- 「
If-None-Match: *只有 GET 能用」——PUT 上表示「只创建不覆盖」。 - 「ETag 必须是内容哈希」——可以是任何字符串,只要变更有变化即可。
自测三问(点开对答案)
1. `If-None-Match` 与 `If-Modified-Since` 同时出现时听谁的? —— 听 `If-None-Match`,`If-Modified-Since` 被完全忽略。 2. ETag 相对 `Last-Modified` 的优势是什么? —— 没有秒级精度限制,同一秒内的变化也能区分。 3. `PUT` 加 `If-None-Match: *` 是什么语义? —— 只创建不覆盖:资源已存在就回 412。读和写的条件都讲完了。还剩一个条件字段,专门给续传用。
- ☐ (本节完,下一节 6.4.12 If-Range)