HTTP 409 Conflict
这是一种状态冲突:请求本身有效,但资源发生了变化,导致当前无法应用该请求。
HTTP 409 的含义
HTTP 409 Conflict 表示服务器理解了请求,但由于该请求与目标资源的当前状态相矛盾,无法应用该请求。典型例子是编辑冲突:两个客户端加载了同一份文档,都进行了保存,第二次保存会悄悄覆盖第一次保存的内容。
与 400 不同,请求本身格式正确——不做修改重新提交,之后甚至可能成功。响应正文应说明具体是什么发生了冲突,以便客户端(或用户)解决问题并重试。
409 错误的常见原因
- 编辑冲突:自客户端上次获取资源以来,该资源已被他人修改(通常通过 ETag / If-Match 体现出来)。
- 创建一个已经存在的资源——重复的用户名、邮箱、slug 或文件名。
- 乐观锁(optimistic locking)中的版本不匹配:提交的版本号已过期。
- 删除或移动一个存在依赖项、而服务器拒绝将其变为孤立状态的资源。
- 并发的工作流步骤被以错误的顺序应用(例如,批准一个已经被取消的订单)。
作为开发者该如何修复
- 阅读响应正文——好的 API 会指明发生冲突的字段或期望的当前版本。
- 重新获取资源,在最新状态之上重新应用你的更改,然后重新提交。
- 使用条件请求(带 ETag 的 If-Match),以便可靠地检测冲突,而不是直接覆盖数据。
- 对于重复创建导致的冲突,应提前决定幂等性(idempotency)策略:返回已存在的资源,或返回 409 并附带指向该资源的信息。
示例响应
HTTP/1.1 409 Conflict
Content-Type: application/json
{"error":"conflict","message":"document was modified by another user","current_version":42}常见问题
409 和 400 有什么区别?
400 表示请求本身格式错误或无效。409 表示请求本身没有问题,但与资源的当前状态发生了冲突——在解决冲突后重试可能会成功。
API 应该在什么情况下返回 409 而不是 422?
对于负载(payload)内部的校验失败使用 422,对于与服务器端现有状态(如重复项或过期版本)的冲突使用 409。
ETag 如何帮助避免 409 错误?
客户端发送带有其最后一次看到的 ETag 的 If-Match;服务器仅在资源未被修改时才应用更改,否则会返回 409(或 412)而不是直接覆盖。