📘 第六章 HTTP首部
📄 一句话概括

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 秒实验

  1. 把两个都给的那组改成 INM 对、IMS 错,看结果(应该是 304)。
  2. 用 PUT 加 If-None-Match: * 试一次已存在的资源,看是不是 412。
  3. 想一想:为什么服务端要同时发 ETag 和 Last-Modified?只发一个不行吗?

常见误区

  1. 「两个都给时以时间更准的那个为准」——以 ETag 为准,与准不准无关。
  2. 「If-None-Match: * 只有 GET 能用」——PUT 上表示「只创建不覆盖」。
  3. 「ETag 必须是内容哈希」——可以是任何字符串,只要变更有变化即可。
自测三问(点开对答案) 1. `If-None-Match` 与 `If-Modified-Since` 同时出现时听谁的? —— 听 `If-None-Match`,`If-Modified-Since` 被完全忽略。 2. ETag 相对 `Last-Modified` 的优势是什么? —— 没有秒级精度限制,同一秒内的变化也能区分。 3. `PUT` 加 `If-None-Match: *` 是什么语义? —— 只创建不覆盖:资源已存在就回 412。

读和写的条件都讲完了。还剩一个条件字段,专门给续传用。

相关笔记