Mezzio错误中间件需注册在错误管道中,确保ErrorHandler位于RouteMiddleware之后、DispatchMiddleware之前;自定义ErrorResponseGenerator返回JSON响应;404等HTTP异常需升级Mezzio或手动判断处理;日志需显式记录异常。

Mezzio 错误中间件不生效?检查是否注册在错误处理管道
Mezzio 的 ErrorResponseGenerator 和 ErrorHandler 只在「错误管道(error pipeline)」中起作用,不是往主请求管道里一塞就管用。常见现象是抛异常后仍返回原始 PHP 错误页面或空响应,根本没走你写的格式化逻辑。
实操要点:
- 确认
config/pipeline.php中调用了$app->pipe(ErrorHandler::class),且该行位于$app->pipe(RouteMiddleware::class)之后、$app->pipe(DispatchMiddleware::class)之前 - 确保没有在其他地方(比如某个路由的中间件链里)提前
return或throw后未被捕获——错误中间件只捕获未被 try/catch 拦截的异常 - 开发环境默认启用
WhoopsErrorHandler,它会覆盖你的自定义格式;上线前务必在config/autoload/error-handler.global.php中禁用whoops并启用json或html渲染器
统一返回 JSON 异常体:替换默认 ErrorResponseGenerator
Mezzio 默认的 ErrorResponseGenerator 返回的是带堆栈的 HTML(开发时)或极简文本(生产),不符合 API 服务对 application/json 响应体的要求。必须手动绑定一个能生成标准结构的实现。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 写一个类实现
ErrorResponseGeneratorInterface,核心是重写__invoke()方法,返回new JsonResponse([ 'error' => $e->getMessage(), 'code' => $e->getCode() ?? 500 ]) - 在
config/autoload/dependencies.global.php中覆盖服务:把ErrorResponseGeneratorInterface::class的工厂指向你的类 - 注意:不要直接修改
ErrorResponseGenerator的构造参数来“凑合”,它内部硬编码了内容类型和模板逻辑,改了也白改
404 / 405 等 HTTP 异常不进 ErrorHandler?它们不算 PHP 异常
NotFoundException、MethodNotAllowedException 是 Mezzio 路由层抛出的 HttpExceptionInterface 实现,但默认 ErrorHandler 只监听 Throwable,而部分 HTTP 异常在早期版本中未被 catch —— 结果就是 404 返回空白页或原始异常信息。
解决路径:
- 升级到 Mezzio 4.9+,其
ErrorHandler已默认兼容HttpExceptionInterface - 若无法升级,在自定义
ErrorResponseGenerator的__invoke()中加判断:if ($e instanceof HttpExceptionInterface) { return new JsonResponse([ 'error' => 'Not found', 'status' => $e->getStatusCode() ], $e->getStatusCode()); } - 别依赖
set_error_handler()去捕获 404,那是 PHP 错误机制,跟 PSR-15 中间件无关
日志与响应脱节:异常被吞掉,看不到真实错误上下文
很多人配完错误中间件后发现:API 返回了干净的 JSON,但日志里啥也没有。这是因为 ErrorHandler 默认只记录级别为 error 的日志,而某些异常(比如 ValidationException)被当成业务异常处理,没触发记录。
关键动作:
- 在自定义
ErrorResponseGenerator的开头显式调用$this->logger->error('Uncaught exception', ['exception' => $e]),别指望中间件自动帮你记全 - 避免在
__invoke()里做耗时操作(如写文件、发 HTTP 请求),它运行在响应已开始输出的阶段,容易超时或阻塞 - 生产环境关闭
display_errors和whoops,否则日志可能重复刷屏,且敏感信息会泄露到响应头中
真正麻烦的是异常分类——HTTP 异常、验证异常、领域异常、第三方 SDK 异常,每种该返回什么状态码、要不要透出详情,得靠 instanceof 一层层判,漏一个就导致前端收不到预期结构。











