代表性面试主题

通用面试:HTTP 428 Precondition Required 如何避免丢失更新?

通用中等
Offer.cc 编辑团队发布 更新

题干

一个 API 对没有 If-Match 的 PUT 返回 428,对 ETag 过期的请求返回 412。请说明两者区别、客户端流程和服务端如何防止 lost update。

题干与适用场景

两个客户端同时编辑同一份文档。服务端要求更新请求携带条件首部:缺少条件时返回 428,条件存在但不再匹配时返回 412。请说明 ETag、If-Match、缓存和重试如何配合,避免后写入覆盖先写入。

面试官考察点

  • 是否理解 428 是“必须带条件”的策略要求,412 是条件已评估但失败。
  • 能否用强 ETag 和 If-Match 建立乐观并发控制。
  • 能否设计读取、编辑、提交、冲突展示和重新合并流程。
  • 能否处理缓存、幂等性、审计和异常重试边界。

回答前需要澄清的问题

  1. 哪些资源和写方法必须条件化,是否允许 If-Match: *
  2. ETag 是强验证器还是弱验证器,是否随每次表示变化?
  3. 客户端遇到 428、412、404 或 409 时分别怎么处理?
  4. 冲突需要字段级合并、人工选择还是直接放弃?
  5. 是否有缓存、版本号、幂等键和审计要求?

30 秒回答框架

客户端先 GET 资源并保存 ETag,编辑后用 If-Match 提交。缺少条件时服务端返回 428,表示客户端必须补条件;条件不匹配时返回 412,表示资源已被修改。客户端应重新读取、展示差异并合并后再提交,不能盲目覆盖。服务端用强 ETag 原子比较并记录版本和审计,缓存必须正确处理条件请求。

分步骤深入解答

第一步:区分 428 与 412

428 来自服务端策略:请求没有所需的前置条件;412 表示请求带了条件,但当前资源状态不满足该条件。两者都不是让客户端直接重试原请求的信号。

第二步:生成资源版本

服务端为表示生成强 ETag,内容或受保护的版本变化时更新它。GET 响应返回 ETag,客户端把它保存为编辑基线,而不是只保存本地时间戳。

第三步:提交 If-Match

客户端在 PUT、PATCH 或其他有副作用的更新中发送 If-Match。服务端在应用写入前原子比较当前 ETag;匹配才执行,否则返回 412 并保持资源不变。

http
GET /documents/42
ETag: "v17"

PUT /documents/42
If-Match: "v17"

第四步:处理冲突

收到 412 后重新 GET,向用户展示服务器版本与本地改动的差异。字段可安全合并时生成新版本;无法自动合并时要求用户选择,不能把旧的 If-Match 再发一次。

第五步:设计缓存行为

条件 GET 可以用 If-None-Match 获得 304,但写入保护使用 If-Match。缓存层不能把旧 ETag 当成当前版本,也不能缓存会泄露资源状态的错误响应。

第六步:处理异常与重试

网络超时后不要直接重放非幂等写入;先查询资源版本或使用幂等键确认结果。428 需要补条件,412 需要重新读取和合并,重试逻辑必须区分这两类动作。

第七步:记录与验证

记录版本、ETag、冲突次数、自动合并率和人工放弃率。用并发更新测试验证“先到者成功、后到者 412”,并确认审计记录能还原每次版本变化。

高质量示范回答

我会让 GET 返回强 ETag,例如 "v17",客户端编辑时保留它。PUT 没有 If-Match 就返回 428,明确提示必须使用条件更新;带有旧 ETag 则在原子比较失败时返回 412。客户端收到 412 后重新 GET,展示服务器与本地差异,合并后用新 ETag 再提交,绝不重复发送旧条件。缓存把 If-None-Match/304 用于读取优化,不能替代写保护。超时的写请求先查询版本或用幂等键确认结果,并监控冲突率和自动合并率。

常见错误

  • 把 428 和 412 都当成服务暂时不可用并无限重试。
  • 使用客户端时间戳代替强 ETag 的原子比较。
  • 收到 412 后直接覆盖服务器版本。
  • 混淆 If-None-Match 的缓存验证与 If-Match 的写入保护。
  • 超时后无条件重放有副作用的写请求。

追问及应对

追问一:为什么不用 Last-Modified?

时间戳精度和时钟问题可能让两个版本看起来相同;强 ETag 直接绑定表示版本,更适合防止丢失更新。

追问二:If-Match: * 什么时候有用?

它表达“资源必须存在”或避免在未知版本上创建/覆盖,具体语义要由 API 文档规定;不能把它当成无条件写入。

追问三:412 与 409 有什么区别?

412 是 HTTP 条件首部未满足;409 表示请求与当前资源状态发生业务冲突。接口可以同时使用,但响应应说明可恢复动作。

追问四:如何做字段级合并?

读取共同基线、服务器版本和本地版本,按字段策略合并;对同一字段的冲突交给用户,并用新 ETag 重新提交。

追问五:缓存返回旧 ETag 怎么办?

对编辑前的 GET 使用适当的缓存控制或 revalidation,提交失败时强制读取源站;服务端仍以当前版本原子比较为准。

追问六:怎样测试并发安全?

让两个客户端读取同一 ETag 并同时写入,验证只有一个成功、另一个收到 412;再覆盖重试、超时、缓存和审计链路。

公开来源

同类题目