应按请求语义分层返回状态码:路径不存在为404,参数错误为400,业务冲突(如重复)为409,成功创建为201并带Location头;Symfony2中路由层判404,控制器内查不到实体也返404,JSON格式错返400,数据库唯一约束捕获后返409。

入库记录时,HTTP 状态码不该只看 Doctrine 是否抛异常,而要按请求语义分层判断:路径存在但参数错是 400,路径根本不存在是 404,业务校验失败(如重复插入)是 409,成功创建才是 201 —— 混用 200 会掩盖路由与业务逻辑的差异。
怎么让 Symfony2 区分 404 和 400?
Symfony2 默认对未匹配路由返回 404,但控制器里手动查不到记录时也容易误返 404。关键在「谁负责判定」:
- 路由层(
Router)只管 URL 是否匹配到某个 controller/action,不关心业务数据是否存在 - 控制器内用
$em->find()或findOneBy()查不到实体,应返回404(资源不存在) - 若请求路径正确(如
POST /api/addresses),但 JSON body 缺字段或格式错(如lat是字符串而非数字),必须返回400 - 不要在控制器开头就
try/catchDoctrine 异常然后统一塞500—— 这会让前端无法区分是网络问题、参数问题还是数据库挂了
入库失败时该用 409 还是 400?
取决于失败原因是否属于「客户端可修正的冲突」:
-
UNIQUE constraint violation(如重复手机号)→409 Conflict,明确告诉前端“这个值已被占用” -
NOT NULL字段为空 →400 Bad Request,说明前端漏传必要字段 - 百度 API 返回
"status": 302(服务暂不可用)→ 不该入库,直接返502 Bad Gateway,并记录日志供运维排查 - 所有数据库级约束错误,都应通过
catch (UniqueConstraintViolationException $e)捕获,而不是靠Validation组件兜底 —— 后者只能防表单,防不住并发写入
为什么不能所有成功都返回 200?
入库操作本质是资源创建,符合 RESTful 原则的响应状态码是 201 Created,附带 Location 头指向新资源:
-
200 OK用于查询或更新已有资源(如PATCH /api/addresses/123) -
201 Created必须搭配Location: /api/addresses/456响应头,方便前端跳转或缓存刷新 - 如果用了
AbstractController,直接 return $this->json($data, 201, ['Location' => $this->generateUrl('app_address_show', ['id' => $address->getId()])]); - 忽略
Location头,等于放弃 REST 接口的自描述能力 —— 前端得硬编码拼 URL,一改路由就崩
真正容易被忽略的是:状态码必须和响应体语义一致。比如返回 201 却塞了个空数组,或 409 时没附带冲突字段名,前端就只能靠猜——这不是接口,是谜语。











