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

If-Unmodified-Since 是「这段时间之后没改过,你才能动它」。改过就回 412。 语义和 If-Modified-Since 正好相反。

📝 本节和前面几节的关系

上一节是读场景的降级,这一节是写场景的时间版,和 6.4.9 的 If-Match 是一对:一个用时间, 一个用标签。

一、两个字段,语义刚好相反

字段 条件成立时 不成立时 典型场景
If-Modified-Since 没改过 → 304 改过 → 200 全量 读
If-Unmodified-Since 没改过 → 执行 改过 → 412 写/删
记法:带 Un 的是「没改过才允许」,不带的是「改过才给我新的」。一个守门,一个送新。
Demo 里不带条件直接放行(204),带最新时间放行(204),带前一天的时间就 412,
正文是「资源已经变了,请重新读一遍」。

二、为什么写场景需要它

典型的例子是删除。用户看到一个列表,点了删除某一行。 但如果这期间那一行已经被别人改过,直接删掉可能是错的——也许它已经变成了别的东西。 加上这个条件,服务端就会回 412 让用户先刷新,确认了再删。 这就是「乐观并发控制」:不锁,但提交时检查版本。 另一个场景是覆盖式更新(PUT 整个资源)。如果客户端读到的是旧版,直接 PUT 会把别人的修改冲掉。 这和 6.4.9 说的丢失更新是同一个问题。

三、为什么不推荐用它代替 If-Match

因为它只有秒级精度。两个人如果在同一秒内各改一次,时间戳看起来一样,检查就失效了。 而 If-Match 用 ETag,可以精确到字节。 所以严格场景应该用 If-Match,If-Unmodified-Since 只是给「只有时间戳可用」的老系统兜底。 还有一个细节:这两个字段之间的优先级, 规范和 If-None-Match/If-Modified-Since 那一对不同——那对是 ETag 优先, 而这一对按 RFC 7232 也是 ETag 优先。所以两者都出现时,服务端该只看 If-Match。

四、动手 Demo:三种条件,看什么时候被 412 挡住

服务端资源最后修改时间写死。 客户端分别不带条件、带最新时间、带前一天的时间去发 DELETE,看各自的判定。

ius = hd.get(u'if-unmodified-since', u'')
if ius and ius != LM:
    return reply(u'HTTP/1.1 412 Precondition Failed',
                 [(u'Last-Modified', LM)],
                 u'资源已经变了,请重新读一遍'.encode(u'utf-8'))
return reply(u'HTTP/1.1 204 No Content', [], b'')

实跑输出:

--------------------------------------------------------------------
If-Unmodified-Since:改过就拒绝
--------------------------------------------------------------------
  资源最后修改               Sat, 24 Oct 2026 12:00:00 GMT

  【不带条件】
    If-Unmodified-Since      (没发)
    结果                     HTTP/1.1 204 No Content | 判定:放行

  【带最新时间】
    If-Unmodified-Since      Sat, 24 Oct 2026 12:00:00 GMT
    结果                     HTTP/1.1 204 No Content | 判定:放行

  【带旧时间】
    If-Unmodified-Since      Fri, 23 Oct 2026 12:00:00 GMT
    结果                     HTTP/1.1 412 Precondition Failed | 判定:412 拒绝:资源在这之后改过
    正文                     资源已经变了,请重新读一遍

  它和 `If-Modified-Since` 正好是反的:
  前者是「**没改过**才让我干」,后者是「**改过**才给我新内容」。
  典型用法是删除或覆盖:客户端读到一个旧版本,
  如果这期间别人改过,就不该让这次删除生效。
  和 `If-Match` 比,它弱一些——时间精度只到秒,
  所以严格的场景应该用 `If-Match` 配 ETag。

不带条件:204 No Content,判定「放行」。204 是「办好了但没内容要回」,删除成功用它最合适。 带最新时间:同样 204 放行。条件成立。 带前一天的时间:412,正文「资源已经变了,请重新读一遍」。 服务端还回了 Last-Modified,让客户端能知道当前版本。 注意这三组都用了 DELETE 方法,但响应里都不带正文——除了那个 412 带了说明文字。

30 秒实验

  1. 把 DELETE 换成 PUT,看语义上有没有区别。
  2. 同时发 If-Match 和 If-Unmodified-Since 且两者矛盾,看服务端该听谁。
  3. 想一想:如果客户端不带任何条件就发 DELETE,服务端该不该拦?

常见误区

  1. 「它和 If-Modified-Since 是一回事」——语义正好相反。
  2. 「不成立时回 304」——回 412。
  3. 「它比 If-Match 更好因为更简单」——精度只有秒,严格场景不够用。
自测三问(点开对答案) 1. `If-Unmodified-Since` 条件不成立时回什么? —— 412 Precondition Failed。 2. 它和 `If-Modified-Since` 的语义差别在哪? —— 前者是「没改过才允许执行」,后者是「改过才给我新内容」。 3. 为什么严格场景该用 `If-Match`? —— ETag 没有秒级精度问题,能精确到字节。

五个条件字段全部讲完。下一个是给排查用的:限制转发跳数。

相关笔记