跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

如何在Symfony中实现统一的API错误响应格式

Symfony默认HTML错误页不适用于API,必须通过kernel.exception事件监听并按Accept头判断是否返回JSON响应,否则会向前端App返回HTML页面;不能用控制器try-catch,否则破坏HTTP语义异常转换、丢失响应头与校验细节。 直接上结论:Symfony 默认的 HTML 错误页对 API 完全不可用,必须拦截
kernel.exception
事件,只对
Accept: application/json
请求做 JSON 化处理,否则会把登录页或 404 页面塞给前端 App。 为什么不能在控制器里 try-catch 每个异常 手动捕获会绕过 Symfony 内核的异常处理链,导致三类问题:
AccessDeniedException
不再自动转成 403,
NotFoundHttpException
不再带标准
X-Content-Type-Options
头 无法复用
Serializer
统一字段结构(比如始终带
error.code
和
error.message
) ValidationException 等校验异常丢失
violations
细节,只能返回笼统的 “Bad Request” 如何注册一个只处理 JSON 请求的异常监听器 核心是检查请求头,不是所有异常都该转 JSON —— 比如管理员访问
/admin
触发 403,不该返回 JSON 而该跳登录页。 监听
kernel.exception
事件,在服务配置中绑定
ApiExceptionListener
在监听器中调用
$request->getAcceptableContentTypes()
,用
in_array('application/json', ...)
判断是否应 JSON 化 对
ValidationFailedException
提取
$e->getViolations()
,生成带
property_path
和
message
的嵌套数组 对
AccessDeniedException
或
NotFoundHttpException
直接映射为 403/404 状态码,不抛新异常
public function onKernelException(ExceptionEvent $event): void { $request = $event->getRequest(); if (!in_array('application/json', $request->getAcceptableContentTypes(), true)) { return; } $exception = $event->getThrowable(); $response = match (true) { $exception instanceof ValidationFailedException => $this->buildValidationResponse($exception), $exception instanceof AccessDeniedException => new JsonResponse(['error' => 'forbidden'], 403), default => new JsonResponse(['error' => 'internal_error'], 500), }; $event->setResponse($response); }
如何让异常响应和正常
$this->json()
保持字段一致 关键点是复用同一个序列化上下文,避免两套逻辑维护不同结构。不要手拼数组,而是统一走
SerializerInterface
。 定义一个序列化组,比如
error:api
,并在错误数据对象上用
#[Groups(['error:api'])]
标记字段 监听器中构造错误对象(非数组),例如
new ApiErrorResponse($code, $message, $details)
调用
$this->serializer->serialize($error, 'json', ['groups' => 'error:api'])
输出 这样
ApiErrorResponse
类就能控制哪些字段出现、是否驼峰、是否默认值,和实体序列化完全一致 最容易被忽略的是 Accept 头解析逻辑 ——
$request->headers->get('Accept')
返回原始字符串,需用
getAcceptableContentTypes()
才能正确处理
application/json, text/plain;q=0.5
这类带质量因子的值。漏掉这点,某些 Postman 或 curl 请求就会意外返回 HTML 错误页。

相关文章