12、Hyperf 3 快速使用 - 自定义异常处理

作者: 温新

分类: 【Hyperf 3 基础系列】

阅读: 801

时间: 2023-04-25 12:27:59

hi,我是温新,一名 PHPer

Hypref 版本:Hyperf 3.0

学习目标:掌握异常的处理

本篇文章将学习两种自定义异常类的使用方法,先开开始吧。

异常在 API 开发中经常使用到,一定要掌握。

方式一:自定义异常类

第一步:定义异常处理器

<?php
// App\Exception\Handler\ApiExceptionHandler.php

namespace App\Exception\Handler;

use App\Exception\ApiException;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Throwable;

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

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

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

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

第二步:定义异常类

<?php
// App\Exception\ApiException.php
namespace App\Exception;

use Hyperf\Server\Exception\ServerException;

class ApiException extends ServerException
{
}

第三步:配置文件中注册异常处理器

<?php
// config/autoload/exceptions.php
    
declare(strict_types=1);

return [
    'handler' => [
        'http' => [
            // 省略
            
            \App\Exception\Handler\ApiExceptionHandler::class,
        ],
    ],
];

第四步:触发异常类

<?php
// App\Controller\IndexController.php
    
use App\Exception\ApiException;
public function index()
{
    throw new ApiException('api error', 500);
}

第五步:测试异常类

$ curl http://localhost:9501/
{"code":500,"message":"api error"}

方式二:通过容器绑定异常类

第一步:创建异常类

<?php

namespace App\Exception;

use Hyperf\Server\Exception\ServerException;

class NewApiException extends ServerException
{
}

第二步:创建统一响应类

<?php

declare(strict_types=1);

namespace App\Components;

use Hyperf\HttpServer\Contract\ResponseInterface;
use Psr\Container\ContainerInterface;

class Response
{
    /**
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @var ResponseInterface
     */
    protected $response;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
        $this->response = $container->get(ResponseInterface::class);
    }

    public function success($data = [])
    {
        return $this->response->json([
            'code' => 0,
            'data' => $data,
        ]);
    }

    public function fail($code, $message = '')
    {
        return $this->response->json([
            'code' => $code,
            'message' => $message,
        ]);
    }
}

第三步:修改 AbstractController 抽象控制器

<?php

declare(strict_types=1);
namespace App\Controller;

use App\Components\Response;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Psr\Container\ContainerInterface;

abstract class AbstractController
{
    #[Inject]
    protected ContainerInterface $container;

    #[Inject]
    protected RequestInterface $request;

    // 此处改为统一响应
    #[Inject]
    protected Response $response;
}

第四步:修改 AppExceptionHandler.php

<?php

declare(strict_types=1);

namespace App\Exception\Handler;

use App\Components\Response;
use App\Exceptions\NewApiException;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Throwable;

class AppExceptionHandler extends ExceptionHandler
{
    public function __construct(protected StdoutLoggerInterface $logger, protected Response $response)
    {
    }

    public function handle(Throwable $throwable, ResponseInterface $response)
    {
        // 处理业务异常
        if ($throwable instanceof NewApiException) {
            return $this->response->fail(101, $throwable->getMessage());
        }

        $this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile()));
        $this->logger->error($throwable->getTraceAsString());
        return $response->withHeader('Server', 'Hyperf')->withStatus(500)->withBody(new SwooleStream('Internal Server Error.'));
    }

    public function isValid(Throwable $throwable): bool
    {
        return true;
    }
}

第五步:响应测试

<?php

declare(strict_types=1);

namespace App\Controller;

use App\Exception\ApiException;
use App\Exception\FooException;
use App\Exceptions\NewApiException;

class IndexController extends AbstractController
{
    public function index()
    {
        throw new FooException('new api error', 800);
    }
}
请登录后再评论