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
使用场景
- 和state-changing methods(例如:POST, PUT, DELETE )一起使用,避免多个user agent操作同一个resource时导致的覆盖问题
- 和safe methods一起使用,当stored response和服务器版本不一致时,终止(abort)请求
场景1
核心逻辑:资源和我当前知道的版本没有变化,才执行修改,以此来避免修改冲突。
假设有A/B它们各自具备如下逻辑:
- A:如果资源版本是v1,就通知服务器修改为v2,如果是其他版本就什么都不做
- B:如果资源版本是v1,就通知服务器修改为v3,如果是其他版本就什么都不做
步骤:
- 服务器当前版本v1
- A/B都去查看服务器版本,都发现是v1
- 由于自身逻辑,A发现刚刚查看的版本为v1,于是告知服务器:将版本修改为v2
- 修改成功,现在服务器版本v2
- 由于自身逻辑,B发现刚刚查看的版本为v1,于是告之服务器:将版本修改为v3
- 修改成功,现在服务器版版本是v3
此时,如果没有If-Match,服务器就会将版本修改为v2,这会违反B自身的逻辑。让我们加上If-Match,修改上述3./4./5./6.步骤:
- 由于自身逻辑,A发现刚刚查看的版本为v1,于是告知服务器:我看到的版本是v1,将版本修改为v2
- 服务器发现当前版本是v1,于是执行修改,现在服务器版本v2
- 由于自身逻辑,B发现刚刚查看的版本为v1,于是告之服务器:我看到的版本是v1,将版本修改为v2
- 服务器发现当前版本是v2,验证不通过,修改失败
场景2
核心逻辑是:“只有当服务器上的资源还是我之前拿到的那一个时,才继续给我发数据。”
例如GET+If-Match保证断点续传的安全性:
假设你正在下载一个 1GB 的游戏安装包。
- 第一次下载: 你请求了文件的前 500MB。
- 服务器返回 206 Partial Content。
- 响应头包含 ETag: "v100"。
- 网络中断: 下载在 500MB 处断开了。
- 尝试续传: 你想要请求剩下的 500MB。
- 风险: 在你断开连接的这段时间里,服务器上的安装包可能更新了(版本变成了 v101)。如果你直接请求剩下的 500MB,拼接到旧的 500MB 后面,整个文件就会损坏。
使用 If-Match 解决: 你的客户端会发送:
GET /game.zip
Range: bytes=500-1000
If-Match: "v100"
- 匹配成功: 说明文件没变,服务器返回剩下的 500MB,下载圆满完成。
- 匹配失败(412 Precondition Failed): 说明文件变了。客户端收到错误后,知道不能续传了,必须从头开始下载整个新文件。
evaluates condition
- false:
- 取值:*
- origin server没有target resource对应的representation
- false:
- 取值:单个entity-tag/entity-tag 列表
- none of the listed tags match the entity-tag of the selected representation
- true:其他情况
evaluates与method执行
验证不通过时返回必须(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,表示“你要的结果已经在那儿了”。