1. 题目与使用场景
一个订单 API 接收 JSON 请求。客户端可能提交语法正确但字段关系不合法的数据,也可能基于旧版本更新订单,或尝试创建已经存在的用户名。请定义 409 与 422 的使用边界,让客户端知道应该修改请求、重新读取资源,还是直接停止重试。
2. 面试官考察点
- 是否能区分“请求内容无法按语义处理”和“请求与资源当前状态冲突”。
- 是否知道 422 的重复请求通常会得到相同结果,不能靠盲目重试解决。
- 是否能把并发控制、幂等重试和错误响应契约联系起来。
- 是否能说明 400、412 等相邻状态码的边界,并保持团队的一致约定。
3. 回答前需要澄清的问题
- API 是否使用 ETag、版本号或其他乐观并发控制?
- “名称已存在”是请求字段规则,还是目标集合当前状态导致的冲突?
- 客户端是否能重新读取资源并展示差异?
- 团队是否已有统一的错误码、字段路径和重试策略?
4. 30 秒回答框架
先看失败原因属于哪一维:如果服务器理解了媒体类型和语法,但请求中的业务语义无法处理,返回 422;如果请求本身可理解,却和目标资源的当前状态发生冲突,返回 409。400 用于无法解析的请求,412 更适合条件请求的前置条件失败。最后补充机器可读错误码、修复建议和并发版本信息。
5. 分步骤深入解答
第一步:定义 422 的边界
422 表示内容类型已理解、语法正确,但其中的指令无法处理。例如日期区间结束早于开始、枚举值不允许,或字段组合违反业务约束。此类错误通常与资源此刻被谁修改无关;客户端应修改 payload 后再提交。
第二步:定义 409 的边界
409 表示请求与目标资源的当前状态冲突。典型场景是版本号落后、订单已经发货却再次取消,或创建操作与现有唯一资源发生冲突。响应应尽量携带当前版本、冲突类型和客户端可采取的下一步。
第三步:处理相邻状态码
JSON 结构损坏、缺少必要语法元素时用 400。带 If-Match 的请求未满足条件时可用 412;这比把所有条件失败都笼统归为 409 更精确。具体选择仍应写入公开 API 契约,避免不同端点各自解释。
第四步:设计重试与错误体
422 不应自动重试同一 payload,因为不修改请求通常会再次失败。409 是否能重试取决于冲突类型:客户端可以重新读取资源、合并变更后重试,但不能在未知状态下无限循环。错误体应包含稳定的 code、字段路径或资源标识、当前版本和修复建议,不记录凭证等敏感信息。
6. 高质量示范回答
我会先判断失败是 payload 语义问题,还是资源状态竞争。日期范围反转、无效枚举和字段组合不成立属于 422;服务器能理解请求,但订单已发货、版本号落后或唯一资源已经存在,属于 409。JSON 无法解析用 400,带 If-Match 的条件不满足可用 412。
>
对 422,我返回稳定业务错误码和字段路径,客户端修改数据后再发。对 409,我返回冲突类型、服务器版本或当前状态,客户端先读取并决定合并、放弃或重新执行。客户端不应对两者都做无条件重试;幂等键只能防重复执行,不能消除并发冲突。所有端点沿用同一份状态码与错误体契约,并监控各类错误的修复率。
7. 常见错误
- 把所有业务校验失败都返回 409,导致客户端误以为重新读取资源即可解决。
- 把版本冲突返回 422,丢失了“当前状态已经变化”的信号。
- 对 422 或 409 进行无上限自动重试,造成请求风暴。
- 只返回人类可读 message,没有稳定错误码、字段路径和修复方向。
- 看到“重复”就固定使用一个状态码,却没有定义它是 payload 规则还是资源状态冲突。
8. 追问及应对
追问一:重复用户名应该是 409 还是 422?
如果 API 把用户名唯一性视为当前集合状态,409 能表达资源状态冲突;如果团队把它定义为字段语义校验,也可以统一使用 422。关键是契约稳定、客户端行为可预测。
追问二:409 一定可以重试吗?
不一定。版本冲突可能在合并后重试,已发货订单的取消冲突则应停止并展示当前状态。响应需要告诉客户端冲突是否可修复,而不是暗示所有 409 都安全重试。
追问三:422 能否用于权限不足?
不能用它替代认证授权语义。未认证通常是 401,已认证但无权限通常是 403;422 应保留给内容语义无法处理的情况。