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)而不是直接覆盖。