控制器内绝大多数情况下不该用 try/catch,应让异常自然冒泡至 kernel.exception 监听器统一处理,以确保日志、监控、响应格式等框架能力生效;仅在必须中断流程且不触发 HTTP 错误的极少数场景才使用。

控制器里抛出的异常,默认会走 kernel.exception 事件链,但直接在控制器里 try/catch 或裸 throw 是常见误用起点——它要么绕过框架处理逻辑,要么暴露不该暴露的信息。
控制器内该不该用 try/catch 包裹业务逻辑
绝大多数情况下,不该。原因很实际:
-
try/catch在控制器里写一次,就少一次被kernel.exception监听器统一记录、上报、转换响应的机会 - 手动
return new JsonResponse(..., 500)容易漏掉 trace_id、日志上下文、环境适配(比如 dev 显示堆栈、prod 隐藏路径) - 若 catch 后又 throw 新异常(比如转成
BadRequestHttpException),反而增加调用栈深度,不利于排查
真正该做的是:让异常自然冒泡,由监听器兜底;只在极少数必须中断流程且不触发 HTTP 错误的场景(如异步任务触发、非关键校验)才用 try/catch。
抛什么异常才能被正确识别为 404 / 403 / 500
Symfony 内置异常类和 HTTP 状态码有明确映射关系,别自己 new \Exception():
- 要 404 页面 → 抛
NotFoundHttpException(不是自定义类,哪怕你写了PostNotFoundException) - 要 403 拒绝访问 → 抛
AccessDeniedHttpException - 要 400 参数错误 → 抛
BadRequestHttpException或ConstraintViolationException(表单验证失败时自动抛) - 要 500 服务错误 → 让未捕获的
RuntimeException或组件异常(如Doctrine\DBAL\Exception\ConnectionException)自然上浮
自定义异常类必须显式 implements ExceptionInterface,否则监听器里 $e instanceof YourCustomException 会失效。
监听器里怎么区分是 API 还是 HTML 请求
靠请求头判断比靠路由前缀更可靠:
- 检查
$event->getRequest()->headers->get('Accept')是否包含application/json - 或用
$event->getRequest()->isXmlHttpRequest()判断是否 AJAX(但注意现代 Fetch 不设 X-Requested-With) - 避免硬编码
if (strpos($request->getPathInfo(), '/api/') === 0)—— 路由前缀可能被反代改写,且不适用于 JSON-RPC 类接口
示例判断逻辑:
<pre class="brush:php;toolbar:false;">if (str_contains($request->headers->get('Accept', ''), 'application/json')) {
$response = new JsonResponse(['code' => 500, 'message' => 'Server error'], 500);
} else {
$response = $this->twig->render('bundles/TwigBundle/Exception/error500.html.twig');
}
$event->setResponse($response);
为什么本地能触发监听器,生产环境却显示 Nginx 50x 页面
这不是 Symfony 的问题,而是 Web 服务器拦截了响应:
- Nginx 默认开启
fastcgi_intercept_errors on,一旦后端返回 5xx,它就直接甩自己的错误页,根本不把响应体传给浏览器 - Apache 有类似行为,对应配置是
ProxyErrorOverride On - 解决方法:在 Nginx 配置中加
fastcgi_intercept_errors off;,并确保error_page 500 /500.html;这类指令没覆盖你的 Symfony 响应
验证方式:curl -I http://yoursite.com/trigger-500,看返回的 Content-Type 是 text/html(Symfony)还是 text/plain(Nginx)。后者说明你还没拿到 Symfony 的响应机会。


















