先记住这个答案
幂等指同一请求执行一次与执行多次,对服务器的预期效果相同。规范上 GET、HEAD、PUT、DELETE、OPTIONS、TRACE 是幂等的,POST、PATCH 不保证。PUT 是整体替换,重复发送同一报文结果不变;DELETE 删除后资源不存在,再删一次状态仍是不存在。因此网络超时或响应丢失时,客户端可以直接重试这些方法而不必担心重复创建或重复扣减,但要注意每次的响应状态码可能不同,比如 DELETE 第一次返回 200,重试返回 404。
- 安全方法全幂等,PUT、DELETE 也幂等
- POST 与 PATCH 不保证幂等
- 幂等重试安全,但响应码可能逐次不同
幂等语义的定义与重试机制
HTTP 规范按客户端意图定义幂等:同一请求发一次和发多次,对服务器资源的预期效果相同。所有安全方法天然幂等,因为读操作不改变资源;PUT 的语义是用请求体整体替换目标资源,同一报文重放后资源内容仍是这份报文,效果不叠加;DELETE 的语义是让资源不存在,删除一次后再删,资源依然不存在,终态一致。
这套语义直接支撑重试机制:当请求因超时、连接重置而结果未知时,客户端对幂等方法可以直接重发,无需先查询状态。即便原请求实际已到达并生效,重放也只是再次到达同一终态。这正是 HTTP 层把重试安全性与方法语义绑定的原因,而 POST 创建子资源时重放会创建多份,所以规范不承诺其幂等。
移动端弱网下重试订单地址更新接口
一个移动端应用调用 PUT /users/42/address 整体覆盖收货地址。弱网环境下客户端发出请求后 5 秒未收到响应,约束是无法确认请求是否到达服务器。决策是带指数退避直接重发同一报文,最多三次。结果是:若原请求已生效,重放只是再次写入相同地址,最终状态正确;若未到达,重试补上写入,两种路径结果一致。
这样处理成立的前提是接口确实按 PUT 语义实现为整体替换而非追加。若服务端把该端点写成在地址列表里新增一条,重试就会产生重复地址,幂等假设被实现打破。团队因此在接口评审中校验:同一报文连续执行两次后资源状态是否与执行一次相同,作为上线检查项。
幂等假设失效的条件与代价
幂等性有三类常见失效条件。其一,服务器不遵守语义,在 DELETE 端点里做计数扣减或在 PUT 端点里追加数据,规范无法强制实现。其二,把响应码当作幂等性判断依据:DELETE 首次返回 200、重试返回 404,响应不同但资源终态一致,仍属幂等,客户端重试逻辑要把 404 视为可接受结果而非失败。其三,并发交错,另一个请求在两次重试之间修改了同一资源,终态可能与预期不同,这不是幂等性能解决的问题。
对应处理:客户端对幂等方法重试时按终态而非单次响应码判断成功,例如 DELETE 重试收到 404 视为已删除。对需要防止并发覆盖的更新,用 If-Match 配合 ETag 做条件请求,冲突时收到 412 再决定重新读取合并。代价是多一次状态确认或冲突处理逻辑,但换得了重试与并发下的确定性。
容易答错的地方
- 认为幂等要求每次响应完全相同
- 幂等只约束服务器端资源的预期效果,不约束响应。DELETE 第一次返回 200,之后返回 404,响应不同但资源终态一致,依然满足幂等定义,重试逻辑应识别这种差异。
- 认为用了 PUT 或 DELETE 就天然安全重试
- 规范只定义语义,不强制实现。若服务器在 DELETE 端点里写审计扣费、在 PUT 端点里追加记录,重试就会产生重复副作用。重试前应确认接口实现确实符合幂等语义。
面试官还会怎么问?
为什么 POST 不幂等却常被要求支持重试?
POST 语义是向服务器提交数据由服务端处理,典型是创建子资源,重放会创建多份,规范因此不承诺幂等。工程上要靠业务层手段如 Idempotency-Key 或唯一约束实现可重试,这已超出 HTTP 方法语义本身。
GET 重试绝对安全吗?
语义上是安全的:GET 是安全且幂等方法,重试不改变资源。但若服务器实现违规,在 GET 处理中写日志计数之外的副作用,重试就有影响。另外 GET 响应可被缓存,重试可能命中缓存而非到达源站。
PUT 重试与 PATCH 重试有何差别?
PUT 整体替换,同一报文重放终态相同,可直接重试。PATCH 携带的是修改操作,若操作是增量式(如数量加一),重放会重复应用导致结果叠加;若补丁本身是幂等操作则可安全重试,但规范不作保证,需逐个确认。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。