Symfony 2.1.13 通过 kernel.exception 事件监听实现统一异常处理,支持自定义异常类(继承 SPL 异常)、监听器注册(YAML 标签绑定)及 exception_controller 兜底,适配 API 与 HTML 响应。

Symfony 2.1.13 虽然版本较老(发布于 2013 年),但其异常处理机制已具备核心能力:通过 kernel.exception 事件监听实现统一拦截,配合自定义异常类与控制器级 fallback,可构建清晰、可控的错误响应流程。关键不在于用新特性,而在于踩准该版本支持的接口和注册方式。
定义符合 Symfony 2.1 的自定义异常类
该版本尚未强制要求 ExceptionInterface(那是 Symfony 3+ 引入的抽象层),但仍需语义明确、便于识别:
- 继承标准 SPL 异常,优先选
\RuntimeException(运行时错误)或\LogicException(逻辑错误),避免裸用\Exception - 类名体现业务含义,如
UserValidationFailedException、PaymentGatewayTimeoutException - 构造函数接收关键参数并生成可读消息,例如:
public function __construct($userId, $reason = '') {
$message = sprintf('User #%d validation failed: %s', $userId, $reason ?: 'unknown');
parent::__construct($message);
}
编写 kernel.exception 监听器
Symfony 2.1.13 完全支持事件监听器模式,这是最推荐的方式:
Miller (mlr) 是一个命令行工具,用于查询、整形和重新格式化名称索引数据,如 CSV、TSV、JSON 和 JSON Lines。它将 awk、sed、cut、join 和 sort 的功能整合到一个专为结构化数据处理而构建的单一工具中。
- 创建监听器类(如
src/Acme/DemoBundle/EventListener/ExceptionListener.php),实现onKernelException方法 - 方法内用
$event->getException()获取异常,判断类型后调用$event->setResponse()返回自定义响应 - 区分请求类型:检查
$request->getRequestFormat()或$request->headers->get('Accept')决定返回 JSON 还是 HTML - 示例响应逻辑:
if ($e instanceof UserValidationFailedException) {
$response = new Response(json_encode(['error' => 'invalid_user', 'message' => $e->getMessage()]), 400);
$response->headers->set('Content-Type', 'application/json');
$event->setResponse($response);
}
在 services.yml 中正确注册监听器
2.1.13 使用 YAML 注册服务,必须显式绑定事件标签:
- 在
src/Acme/DemoBundle/Resources/config/services.yml中添加:
acme.demo.exception_listener:
class: Acme\DemoBundle\EventListener\ExceptionListener
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
- 确保命名空间路径与文件物理位置严格一致(注意大小写)
- 不要漏掉
method字段,2.1 不支持自动方法推断
备选方案:配置 exception_controller(适用于页面错误)
若监听器未覆盖全部场景(如模板渲染失败),可启用内置异常控制器作为兜底:
- 在
app/config/config.yml中设置:
twig:
exception_controller: 'AcmeDemoBundle:Exception:show' - 对应控制器方法需接收
FlattenException $exception和$logger参数(Symfony 2.1 已提供FlattenException) - 该控制器只处理 HTML 请求,适合定制 404/500 页面,不适用于 API

















