3.1. If-Match

3 阅读5分钟

3.1. If-Match

rfc 7232 3.1. If-Match

If-Match被忽略的情况

caches and intermediaries应该(can)忽略If-Match,因为If-Match在定义上来说不适用于stored response

取值

If-Match = "*" / 1#entity-tag

Examples:
If-Match: "xyzzy"
If-Match: "xyzzy", "r2d2xxxx", "c3piozzzz"
If-Match: *

entity-tags对比方式

必须(MUST)使用strong comparison function

使用场景

  1. 和state-changing methods(例如:POST, PUT, DELETE )一起使用,避免多个user agent操作同一个resource时导致的覆盖问题
  2. 和safe methods一起使用,当stored response和服务器版本不一致时,终止(abort)请求

场景1

核心逻辑:资源和我当前知道的版本没有变化,才执行修改,以此来避免修改冲突。

假设有A/B它们各自具备如下逻辑:

  • A:如果资源版本是v1,就通知服务器修改为v2,如果是其他版本就什么都不做
  • B:如果资源版本是v1,就通知服务器修改为v3,如果是其他版本就什么都不做

步骤:

  1. 服务器当前版本v1
  2. A/B都去查看服务器版本,都发现是v1
  3. 由于自身逻辑,A发现刚刚查看的版本为v1,于是告知服务器:将版本修改为v2
  4. 修改成功,现在服务器版本v2
  5. 由于自身逻辑,B发现刚刚查看的版本为v1,于是告之服务器:将版本修改为v3
  6. 修改成功,现在服务器版版本是v3

此时,如果没有If-Match,服务器就会将版本修改为v2,这会违反B自身的逻辑。让我们加上If-Match,修改上述3./4./5./6.步骤:

  1. 由于自身逻辑,A发现刚刚查看的版本为v1,于是告知服务器:我看到的版本是v1,将版本修改为v2
  2. 服务器发现当前版本是v1,于是执行修改,现在服务器版本v2
  3. 由于自身逻辑,B发现刚刚查看的版本为v1,于是告之服务器:我看到的版本是v1,将版本修改为v2
  4. 服务器发现当前版本是v2,验证不通过,修改失败

场景2

核心逻辑是:“只有当服务器上的资源还是我之前拿到的那一个时,才继续给我发数据。”

例如GET+If-Match保证断点续传的安全性:

假设你正在下载一个 1GB 的游戏安装包。

  1. 第一次下载: 你请求了文件的前 500MB。
  • 服务器返回 206 Partial Content。
  • 响应头包含 ETag: "v100"。
  1. 网络中断: 下载在 500MB 处断开了。
  2. 尝试续传: 你想要请求剩下的 500MB。
  3. 风险: 在你断开连接的这段时间里,服务器上的安装包可能更新了(版本变成了 v101)。如果你直接请求剩下的 500MB,拼接到旧的 500MB 后面,整个文件就会损坏。

使用 If-Match 解决: 你的客户端会发送:

GET /game.zip
Range: bytes=500-1000
If-Match: "v100"
  • 匹配成功: 说明文件没变,服务器返回剩下的 500MB,下载圆满完成。
  • 匹配失败(412 Precondition Failed): 说明文件变了。客户端收到错误后,知道不能续传了,必须从头开始下载整个新文件。

evaluates condition

  1. false:
    • 取值:*
    • origin server没有target resource对应的representation
  2. false:
    • 取值:单个entity-tag/entity-tag 列表
    • none of the listed tags match the entity-tag of the selected representation
  3. true:其他情况

evaluates与method执行

参考5. Evaluation

验证不通过时返回必须(MUST)返回的code

412(Precondition Failed)

只要If-Match验证不通过,就可以返回412。rfc中并没有定义当返回412时response的中其他部分的规则,参考4.2 Precondition Failed

2xx(Successful)

如果If-Match验证不通过,并且服务器发现target resource是客户端期望的状态,此时服务器可以返回2xx。

如果服务器不能证明多次修改是来自于同一个user agent,它就不能(MUST NOT)发送etag.

关于返回的body,取决于具体的2xx(gemin)

举例

1. 返回 412 (Precondition Failed) 场景

场景描述:真正的并发冲突。用户尝试将资源从 v1 修改为 v2,但服务器已被他人修改为 v3。

客户端请求

PATCH /documents/123 HTTP/1.1
Host: api.example.com
If-Match: "v1"
Content-Type: application/json

{
  "content": "Hello Alice"
}

服务器响应

HTTP/1.1 412 Precondition Failed
ETag: "v3"
Content-Type: application/json

{
  "error": "Conflict",
  "message": "The resource has been modified by another user. Please refresh.",
  "current_version": "v3"
}

逻辑说明

服务器发现 If-Match 的 "v1" 不等于当前的 "v3",且请求的内容 "Hello Alice" 也不是当前的状态。因此拒绝执行,并返回最新的 ETag 协助客户端恢复。

2. 返回 2xx (Successful) 场景

场景描述

幂等性优化(如网络重试)。用户尝试修改资源,虽然版本号不对,但目标状态已达成。

客户端请求(第一次成功但超时,这是第二次重试)

PUT /documents/123 HTTP/1.1
Host: api.example.com
If-Match: "v1"
Content-Type: application/json

{
  "content": "Hello World"
}

服务器响应

HTTP/1.1 204 No Content
Content-Location: /documents/123

(注:根据 RFC 7232,此处不应包含 ETag 响应头)

逻辑说明: 服务器发现 If-Match 的 "v1" 虽然与当前的 "v2" 不符,但检查发现文档内容已经是 "Hello World" 了。为了不向用户报错,服务器选择返回 204,表示“你要的结果已经在那儿了”。