异常处理器

Hyperf 里,业务代码都运行在 Worker进程 上,也就意味着一旦任意一个请求的业务存在没有捕获处理的异常的话,都会导致对应的 Worker进程 被中断退出,虽然被中断的 Worker进程 仍会被重新拉起,但对服务而言也是不能接受的,且捕获异常并输出合理的报错内容给客户端也是更加友好的。
我们可以通过对各个 server 定义不同的 异常处理器(ExceptionHandler),一旦业务流程存在没有捕获的异常,都会被传递到已注册的 异常处理器(ExceptionHandler) 去处理。

关于,如何自定义异常,如何注册异常,这里就不粘贴了,详细的直接看 Hyperf 文档。 下面,贴合一个实际例子讲解下,如何处理异常。

引入 phper666/jwt-auth

安装

composer require phper666/jwt-auth:~2.0.1

发布配置

php bin/hyperf.php jwt:publish --config

配置 .env

# 务必改为你自己的字符串
JWT_SECRET=hyperf
#token过期时间,单位为秒
JWT_TTL=60

使用

<?php

declare(strict_types=1);

namespace App\Controller;

use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\AutoController;
use \Phper666\JwtAuth\Jwt;

/**
 * @AutoController()
 * Class IndexController
 * @package App\Controller
 */
class IndexController extends AbstractController
{
    /**
     * @Inject()
     * @var Jwt
     */
    protected $jwt;

    # 模拟登录
    public function login()
    {
        $username = $this->request->input('username');
        $password = $this->request->input('password');
        if ($username && $password) {
            $userData = [
                'uid' => 1, // 如果使用单点登录,必须存在配置文件中的sso_key的值,一般设置为用户的id
                'username' => 'xx',
            ];
            $token = $this->jwt->getToken($userData);
            $data = [
                'code' => 0,
                'msg' => 'success',
                'data' => [
                    'token' => (string)$token,
                    'exp' => $this->jwt->getTTL(),
                ]
            ];
            return $this->response->json($data);
        }
        return $this->response->json(['code' => 0, 'msg' => '登录失败', 'data' => []]);
    }

    # 刷新token,http头部必须携带token才能访问的路由
    public function refreshToken()
    {
        $token = $this->jwt->refreshToken();
        $data = [
            'code' => 0,
            'msg' => 'success',
            'data' => [
                'token' => (string)$token,
                'exp' => $this->jwt->getTTL(),
            ]
        ];
        return $this->response->json($data);
    }

    # 注销token,http头部必须携带token才能访问的路由
    public function logout()
    {
        $this->jwt->logout();
        return true;
    }

    # http头部必须携带token才能访问的路由
    public function getData()
    {
        $data = [
            'code' => 0,
            'msg' => 'success',
            'data' => [
                'cache_time' => $this->jwt->getTokenDynamicCacheTime() // 获取token的有效时间,动态的
            ]
        ];
        return $this->response->json($data);
    }

    public function index()
    {
        $user = $this->request->input('user', 'Hyperf');
        $method = $this->request->getMethod();

        return [
            'method' => $method,
            'message' => "Hello {$user}.",
        ];
    }
}

验证

编辑路由文件

Router::addGroup('/v1', function () {
    Router::get('/data', 'App\Controller\IndexController@getData');
}, ['middleware' => [Phper666\JwtAuth\Middleware\JwtAuthMiddleware::class]]);

测试

请求 登陆,返回 token

file

正常情况下,token 有效、并且正确传入:

file

那么如果不传递 token、或者 token 失效,jwt-auth 这个组件只是抛出了异常,并在控制台打印出来,,但是用户并不知情。报出 500 的响应

file

这样肯定不行哈,所以异常处理这个东西就派上用场了。

我们看到在 vendor/phper666/jwt-auth/src/Exception 会有两个异常类。

自定义异常

接下来我们就自定义一下异常处理

touch app/Exception/Handler/JtwExceptionHandler.php

编辑如下代码,主要捕获的就是 Phper666\JwtAuth\Exception\TokenValidException 类。

<?php
namespace App\Exception\Handler;

use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Phper666\JwtAuth\Exception\TokenValidException;
use Psr\Http\Message\ResponseInterface;
use Throwable;

class JtwExceptionHandler extends  ExceptionHandler
{
    public function handle(Throwable $throwable, ResponseInterface $response)
    {
        // 判断被捕获到的异常是希望被捕获的异常
        if ($throwable instanceof TokenValidException) {
            // 格式化输出
            $data = json_encode([
                'code' => $throwable->getCode(),
                'message' => $throwable->getMessage(),
            ], JSON_UNESCAPED_UNICODE);

            // 阻止异常冒泡
            $this->stopPropagation();
            return $response->withStatus(500)->withBody(new SwooleStream($data));
        }

        // 交给下一个异常处理器
        return $response;

        // 或者不做处理直接屏蔽异常
    }

    /**
     * 判断该异常处理器是否要对该异常进行处理
     * @param Throwable $throwable
     * @return bool
     */
    public function isValid(Throwable $throwable): bool
    {
        return true;
    }
}

注册异常

编辑配置文件 config/autoload/exceptions.php

<?php

declare(strict_types=1);

use Hyperf\Validation\ValidationExceptionHandler;

return [
    'handler' => [
        'http' => [
            App\Exception\Handler\AppExceptionHandler::class,
            \App\Exception\Handler\JtwExceptionHandler::class,
        ],
    ],
];

然后在测试一下

file

ok,这就是关于异常的处理。

暂无回复
0 / 180
网站公告

LaravelCode 正式上线啦

--->>>体验新站

今日诗词
正在加载今日诗词....
标签云
PHP Laravel API 随想 Node.js Linux Yii2.0 Redis 队列 JavaScript Vue Java MySQL 微信小程序 Python Hyperf 转载