Hyperf异常处理器中不能直接调用view(),因视图组件未初始化且辅助函数未注册;需先安装hyperf/view、配置view.php,再通过app('view')->fetch()获取HTML字符串并用SwooleStream封装返回。

Hyperf 本身不支持“错误页模板”这种传统 Web 框架的概念,它默认面向 API 场景,返回 JSON;要渲染 HTML 模板,必须手动引入视图组件并显式调用渲染逻辑——否则 render() 方法里直接写 view() 会报错或静默失败。
为什么 ExceptionHandler::handle() 不能直接调用 view()
Hyperf 的异常处理器运行在请求生命周期早期,此时 hyperf/view 组件尚未初始化,app('view') 可能为 null,view() 辅助函数也未注册。常见错误现象是:Call to undefined function view() 或 Container does not bind interface: Hyperf\Contract\ViewInterface。
- 必须先执行
composer require hyperf/view安装视图组件 - 确保
config/autoload/view.php存在且'view_path'指向可读目录(如resources/views) - 在
handle()中不要用view('error/500'),改用容器获取实例:app('view')->fetch('error/500', $data) - 注意:fetch 返回的是字符串,需包裹进
SwooleStream才能塞进 Response
如何在 handle() 中返回 HTML 错误页而非 JSON
Hyperf 默认对所有请求都走 JSON 响应,要区分是否渲染 HTML,得靠请求头判断。比如只对浏览器发起的非 AJAX 请求返回模板,其余仍走 JSON。
- 用
$request->header('accept')判断是否含text/html,比isAjax()更可靠(Hyperf 中无该方法) - 模板路径建议用绝对路径写法:
resources/views/error/500.blade.php(若用 Blade)或resources/views/error/500.html(若用 Smarty/Twig) - 状态码必须显式设置:
$response->withStatus(500),否则默认是 200 - 示例片段:
use Hyperf\HttpMessage\Stream\SwooleStream; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; public function handle(Throwable $throwable, ResponseInterface $response) { $request = context_get(ServerRequestInterface::class); $isHtmlRequest = str_contains((string) $request?->header('accept'), 'text/html'); if ($isHtmlRequest && $throwable instanceof \RuntimeException) { $html = app('view')->fetch('error/500', [ 'message' => $throwable->getMessage(), 'file' => $throwable->getFile(), 'line' => $throwable->getLine(), ]); $this->stopPropagation(); return $response->withStatus(500)->withBody(new SwooleStream($html)); } // 兜底返回 JSON $data = json_encode(['code' => 500, 'message' => 'Internal Error'], JSON_UNESCAPED_UNICODE); $this->stopPropagation(); return $response->withStatus(500)->withBody(new SwooleStream($data)); }
isValid() 返回 true 的后果与风险
这个方法决定当前处理器是否“接管”该异常。如果无条件返回 true,会导致后续注册的处理器(比如 ValidationExceptionHandler)完全失效,所有异常都被你这一个类吃掉。
- 务必做类型判断:
return $throwable instanceof \App\Exception\BusinessException; - 若想兜底处理未被其他处理器捕获的异常,应放在
exceptions.php配置数组的末尾,并在isValid()中返回false表示“不处理”,让流程继续向下传递 - 调试时可临时加日志:
var_dump(get_class($throwable));,但上线前必须删掉 - 别在
handle()里抛出新异常,会导致无限递归崩溃
真正容易被忽略的是:Hyperf 的视图渲染依赖完整的中间件链和容器上下文,而异常发生时这些可能已中断。所以哪怕 view() 能调通,模板里用到的 @auth、@section 等指令也可能因上下文缺失而报错——这不是配置问题,是架构限制。要么接受纯静态错误页,要么把错误页逻辑移到正常请求流程中(例如 404 重定向到 /error/404)。


















