2019-06-04 19:09:36 +08:00
|
|
|
# 异常处理器
|
|
|
|
|
2019-11-14 01:44:09 +08:00
|
|
|
在 `Hyperf` 里,业务代码都运行在 `Worker 进程` 上,也就意味着一旦任意一个请求的业务存在没有捕获处理的异常的话,都会导致对应的 `Worker 进程` 被中断退出,这对服务而言也是不能接受的,捕获异常并输出合理的报错内容给客户端也是更加友好的。
|
2019-08-16 19:56:30 +08:00
|
|
|
我们可以通过对各个 `server` 定义不同的 `异常处理器(ExceptionHandler)`,一旦业务流程存在没有捕获的异常,都会被传递到已注册的 `异常处理器(ExceptionHandler)` 去处理。
|
2019-06-04 19:09:36 +08:00
|
|
|
|
2019-06-24 11:13:47 +08:00
|
|
|
## 自定义一个异常处理
|
|
|
|
|
|
|
|
### 注册异常处理器
|
2019-06-04 19:09:36 +08:00
|
|
|
|
|
|
|
目前仅支持配置文件的形式注册 `异常处理器(ExceptionHandler)`,配置文件位于 `config/autoload/exceptions.php`,将您的自定义异常处理器配置在对应的 `server` 下即可:
|
|
|
|
|
|
|
|
```php
|
|
|
|
<?php
|
|
|
|
// config/autoload/exceptions.php
|
|
|
|
return [
|
|
|
|
'handler' => [
|
|
|
|
// 这里的 http 对应 config/autoload/server.php 内的 server 所对应的 name 值
|
|
|
|
'http' => [
|
|
|
|
// 这里配置完整的类命名空间地址已完成对该异常处理器的注册
|
2019-06-25 11:38:11 +08:00
|
|
|
\App\Exception\Handler\FooExceptionHandler::class,
|
2019-06-04 19:09:36 +08:00
|
|
|
],
|
|
|
|
],
|
|
|
|
];
|
|
|
|
```
|
|
|
|
|
|
|
|
> 每个异常处理器配置数组的顺序决定了异常在处理器间传递的顺序。
|
|
|
|
|
2019-06-24 11:13:47 +08:00
|
|
|
### 定义异常处理器
|
2019-06-04 19:09:36 +08:00
|
|
|
|
2019-07-01 01:48:30 +08:00
|
|
|
我们可以在任意位置定义一个 `类(Class)` 并继承抽象类 ` Hyperf\ExceptionHandler\ExceptionHandler` 并实现其中的抽象方法,如下:
|
2019-06-04 19:09:36 +08:00
|
|
|
|
|
|
|
```php
|
|
|
|
<?php
|
2019-06-24 01:19:38 +08:00
|
|
|
namespace App\Exception\Handler;
|
2019-06-04 19:09:36 +08:00
|
|
|
|
2019-06-24 01:19:38 +08:00
|
|
|
use Hyperf\ExceptionHandler\ExceptionHandler;
|
2019-06-04 19:09:36 +08:00
|
|
|
use Hyperf\HttpMessage\Stream\SwooleStream;
|
2019-06-04 19:31:25 +08:00
|
|
|
use Psr\Http\Message\ResponseInterface;
|
2019-06-25 11:38:11 +08:00
|
|
|
use App\Exception\FooException;
|
2019-06-04 19:09:36 +08:00
|
|
|
use Throwable;
|
|
|
|
|
2019-06-25 11:38:11 +08:00
|
|
|
class FooExceptionHandler extends ExceptionHandler
|
2019-06-04 19:09:36 +08:00
|
|
|
{
|
|
|
|
public function handle(Throwable $throwable, ResponseInterface $response)
|
|
|
|
{
|
2019-06-24 11:13:47 +08:00
|
|
|
// 判断被捕获到的异常是希望被捕获的异常
|
2019-06-25 11:38:11 +08:00
|
|
|
if ($throwable instanceof FooException) {
|
2019-06-24 11:13:47 +08:00
|
|
|
// 格式化输出
|
2019-06-04 19:09:36 +08:00
|
|
|
$data = json_encode([
|
|
|
|
'code' => $throwable->getCode(),
|
|
|
|
'message' => $throwable->getMessage(),
|
|
|
|
], JSON_UNESCAPED_UNICODE);
|
2019-06-24 17:28:13 +08:00
|
|
|
|
|
|
|
// 阻止异常冒泡
|
|
|
|
$this->stopPropagation();
|
2019-06-04 19:09:36 +08:00
|
|
|
return $response->withStatus(500)->withBody(new SwooleStream($data));
|
|
|
|
}
|
2019-06-24 11:13:47 +08:00
|
|
|
|
2019-06-24 17:28:13 +08:00
|
|
|
// 交给下一个异常处理器
|
2019-08-23 17:15:48 +08:00
|
|
|
return $response;
|
2019-06-24 17:28:13 +08:00
|
|
|
|
2019-06-26 18:01:25 +08:00
|
|
|
// 或者不做处理直接屏蔽异常
|
2019-06-04 19:09:36 +08:00
|
|
|
}
|
2019-06-24 01:19:38 +08:00
|
|
|
|
2019-06-26 18:01:25 +08:00
|
|
|
/**
|
|
|
|
* 判断该异常处理器是否要对该异常进行处理
|
|
|
|
*/
|
2019-06-24 01:19:38 +08:00
|
|
|
public function isValid(Throwable $throwable): bool
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2019-06-04 19:09:36 +08:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2019-06-24 11:13:47 +08:00
|
|
|
### 定义异常类
|
|
|
|
|
2019-06-24 01:19:38 +08:00
|
|
|
```php
|
|
|
|
<?php
|
2019-06-24 11:13:47 +08:00
|
|
|
namespace App\Exception;
|
2019-06-24 01:19:38 +08:00
|
|
|
|
2019-06-24 11:13:47 +08:00
|
|
|
use App\Constants\ErrorCode;
|
|
|
|
use Hyperf\Server\Exception\ServerException;
|
2019-06-24 01:19:38 +08:00
|
|
|
use Throwable;
|
|
|
|
|
2019-06-25 11:38:11 +08:00
|
|
|
class FooException extends ServerException
|
2019-06-24 01:19:38 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2019-06-26 18:01:25 +08:00
|
|
|
### 触发异常
|
2019-06-24 11:13:47 +08:00
|
|
|
|
2019-06-24 01:19:38 +08:00
|
|
|
```php
|
2019-06-24 11:13:47 +08:00
|
|
|
|
|
|
|
namespace App\Controller;
|
2019-06-24 01:19:38 +08:00
|
|
|
|
2019-06-26 18:01:25 +08:00
|
|
|
use App\Exception\FooException;
|
2019-06-24 12:54:25 +08:00
|
|
|
|
2019-06-24 11:13:47 +08:00
|
|
|
class IndexController extends Controller
|
|
|
|
{
|
2019-06-24 01:19:38 +08:00
|
|
|
public function index()
|
|
|
|
{
|
2019-06-25 11:38:11 +08:00
|
|
|
throw new FooException('Foo Exception...', 800);
|
2019-06-24 01:19:38 +08:00
|
|
|
}
|
2019-06-24 11:13:47 +08:00
|
|
|
}
|
2019-06-24 01:19:38 +08:00
|
|
|
|
|
|
|
```
|
2019-06-25 11:38:11 +08:00
|
|
|
在上面这个例子,我们先假设 `FooException` 是存在的一个异常,以及假设已经完成了该处理器的配置,那么当业务抛出一个没有被捕获处理的异常时,就会根据配置的顺序依次传递,整一个处理流程可以理解为一个管道,若前一个异常处理器调用 `$this->stopPropagation()` 则不再往后传递,若最后一个配置的异常处理器仍不对该异常进行捕获处理,那么就会交由 Hyperf 的默认异常处理器处理了。
|
2019-11-10 14:22:33 +08:00
|
|
|
|
2020-05-11 22:46:30 +08:00
|
|
|
## 集成 Whoops
|
|
|
|
|
|
|
|
框架提供了 Whoops 集成。
|
|
|
|
|
|
|
|
首先安装 Whoops
|
|
|
|
```php
|
|
|
|
composer require --dev filp/whoops
|
|
|
|
```
|
|
|
|
|
|
|
|
然后配置 Whoops 专用异常处理器。
|
|
|
|
|
|
|
|
```php
|
|
|
|
// config/autoload/exceptions.php
|
|
|
|
return [
|
|
|
|
'handler' => [
|
|
|
|
'http' => [
|
|
|
|
\Hyperf\ExceptionHandler\Handler\WhoopsExceptionHandler::class,
|
|
|
|
],
|
|
|
|
],
|
|
|
|
];
|
|
|
|
```
|
|
|
|
|
|
|
|
效果如图:
|
|
|
|
|
|
|
|
![whoops](/imgs/whoops.png)
|
|
|
|
|
|
|
|
|
2019-11-11 01:57:01 +08:00
|
|
|
## Error 监听器
|
2019-11-10 14:22:33 +08:00
|
|
|
|
2019-11-11 09:45:50 +08:00
|
|
|
框架提供了 `error_reporting()` 错误级别的监听器 `Hyperf\ExceptionHandler\Listener\ErrorExceptionHandler`。
|
2019-11-10 14:22:33 +08:00
|
|
|
|
|
|
|
### 配置
|
|
|
|
|
2019-11-11 01:57:01 +08:00
|
|
|
在 `config/autoload/listeners.php` 中添加监听器
|
2019-11-10 14:22:33 +08:00
|
|
|
|
|
|
|
```php
|
|
|
|
<?php
|
|
|
|
return [
|
2019-11-11 09:45:50 +08:00
|
|
|
\Hyperf\ExceptionHandler\Listener\ErrorExceptionHandler::class
|
2019-11-10 14:22:33 +08:00
|
|
|
];
|
|
|
|
```
|
|
|
|
|
2019-11-11 01:57:01 +08:00
|
|
|
则当出现类似以下的代码时会抛出 `\ErrorException` 异常
|
2019-11-10 14:22:33 +08:00
|
|
|
|
|
|
|
```php
|
|
|
|
<?php
|
|
|
|
try {
|
|
|
|
$a = [];
|
|
|
|
var_dump($a[1]);
|
|
|
|
} catch (\Throwable $throwable) {
|
|
|
|
var_dump(get_class($throwable), $throwable->getMessage());
|
|
|
|
}
|
|
|
|
|
|
|
|
// string(14) "ErrorException"
|
|
|
|
// string(19) "Undefined offset: 1"
|
|
|
|
```
|
|
|
|
|
2019-11-11 01:57:01 +08:00
|
|
|
如果不配置监听器则如下,且不会抛出异常。
|
2019-11-10 14:22:33 +08:00
|
|
|
|
|
|
|
```
|
|
|
|
PHP Notice: Undefined offset: 1 in IndexController.php on line 24
|
|
|
|
|
|
|
|
Notice: Undefined offset: 1 in IndexController.php on line 24
|
|
|
|
NULL
|
|
|
|
```
|
|
|
|
|