应使用JsonResponse类而非手动json_encode()+Response,因其自动设置Content-Type、处理null/循环引用、禁用调试头;Doctrine实体须经toArray()或DTO投影后传入,避免N+1和栈溢出。

直接用 JsonResponse 类返回,别手写 json_encode() + Response。它自动设好 Content-Type: application/json、UTF-8 编码,还安全处理 null、NAN、循环引用等边缘情况。
为什么不能用 Response 包裹 json_encode()
常见错误是手动拼 JSON 字符串再塞进 Response,结果漏设 header 或编码不一致,前端收不到合法 JSON;更糟的是在已输出内容后调用 echo 或 var_dump(),触发 “headers already sent” 错误。
-
JsonResponse内部调用json_encode($data, JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE),确保中文不乱码、非法 UTF-8 字符被静默替换 - 它会自动禁用
X-Debug-Token等调试头(API 场景下不该暴露) - 传入
null时返回null字符串,而不是抛JSON_ERROR_INF异常
怎么安全序列化 Doctrine 实体
实体对象不能直接扔进 JsonResponse 构造函数——默认会触发懒加载、N+1 查询,或因 User ↔ Post 循环引用导致栈溢出。
- 最简方案:在实体里加
toArray()方法,只返回基础字段(如id、name),不调用关联 getter - 推荐方案:用 DTO 手动投影,比如
UserDto::fromEntity($user),彻底解耦 API 输出结构与数据库模型 - 慎用
Serializer:若必须用,需配ObjectNormalizer+JsonEncoder,并显式设circular_reference_limit,否则开发环境不报错、生产环境偶发 500 - 绝对禁用
get_object_vars($entity):它会暴露私有属性、代理对象内部字段(如__isInitialized__)
怎么统一处理 API 异常并返回 JSON
Symfony 默认异常页面是 HTML,对 API 请求必须拦截并转成 JSON 响应。靠 kernel.exception 事件监听最稳,别在每个控制器里写 try-catch。
- 只对
Accept: application/json或路由以/api/开头的请求做 JSON 转换,避免影响普通页面跳转 - 监听器里检查
$event->getRequest()->headers->get('Accept')是否含application/json,不是就跳过 - 对
ValidationException返回400+['error' => 'validation_failed', 'details' => [...]],对AccessDeniedException返回403 - 别全局替换异常响应——HTML 页面仍需渲染 404 模板,JSON 接口才需要结构化错误体
最容易被忽略的是格式判断逻辑:仅靠 $request->getRequestFormat() 不可靠,因为 URL 后缀(如 .json)或 _format 参数可能被覆盖;必须结合 Accept 头 + 路由前缀双重校验,否则调试时看着是 JSON,上线后某个客户端没带 header 就崩成 HTML 错误页。


















