Hyperf 的异常处理由 ExceptionHandler 机制负责,而非中间件;未捕获异常会跳过中间件链,直接交由配置中按序匹配的 ExceptionHandler 处理,需手动设置状态码并调用 stopPropagation() 防止冒泡。

Hyperf 中的异常处理不是靠中间件来“拦截并展示”程序异常的,而是由专门的 ExceptionHandler 机制负责。中间件不处理未捕获的异常,它只在请求正常流转过程中执行逻辑;一旦业务代码抛出未捕获异常,控制权会立即交由已注册的异常处理器,跳过所有后续中间件和控制器。
异常处理走的是独立通道,不是中间件链
Hyperf 的请求生命周期中,中间件、控制器、响应生成属于“正向流程”,而异常是中断流——它不经过中间件的 process() 方法,也不会被洋葱模型包裹。未捕获异常会直接触发框架的异常分发器,按 config/autoload/exceptions.php 中定义的顺序查找匹配的 ExceptionHandler。
- 中间件里 不能 拦截业务层抛出的未捕获异常(比如你在 Controller 里
throw new RuntimeException(),AuthMiddleware 或 LoggingMiddleware 都收不到) - 想记录或修改异常行为,必须写
ExceptionHandler类,并在配置中注册 - 中间件内若自己抛异常(如鉴权失败 throw new UnauthorizedException),这个异常会被后续的 ExceptionHandler 捕获,但该中间件本身已退出,不参与响应构造
正确做法:用 ExceptionHandler 捕获 + 格式化响应
你需要创建一个继承 Hyperf\ExceptionHandler\ExceptionHandler 的类,实现 handle() 和 isValid():
-
isValid()必须精确判断异常类型,例如return $throwable instanceof AppException\BusinessException;,避免误吞 404、422 等框架标准异常 -
handle()中要调用$this->stopPropagation(),否则异常会继续冒泡,可能触发 Worker 进程崩溃 - HTTP 状态码需手动设置:
$response->json([...], 400),框架不会自动映射异常类型到 status - 业务错误码建议放在 JSON body 中(如
"code": 1001),而非复用 HTTP 状态码,便于前端统一解析
关键配置顺序不能错
在 config/autoload/exceptions.php 中,http 数组的顺序就是匹配优先级:
- 必须把
Hyperf\HttpServer\Exception\HttpExceptionHandler::class放在最前面——它是处理 404、405、422 等标准 HTTP 异常的兜底处理器 - 你的
AppExceptionHandler::class应紧随其后,专用于捕获自定义业务异常 - 如果把自定义 Handler 放在 HttpExceptionHandler 前面,且
isValid()返回true,那么所有 404 请求都会被你转成 500 响应,前端拿不到正确状态码
调试时注意真实出口
当看到“程序异常被展示出来”,实际发生的是:
- Worker 进程中某次协程执行抛出未捕获异常
- 框架调用匹配的
ExceptionHandler::handle() - 该方法返回
ResponseInterface,最终经 Swoole 发送给客户端 - 日志中的异常堆栈,应从容器中取当前请求上下文(如 URI、IP、trace_id)结构化记录,而不是只打
getTraceAsString()


















