Files
hdk-core/src/Exceptions/Handler/CommonHandler.php
2023-01-13 22:39:08 +08:00

205 lines
8.5 KiB
PHP

<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Singularity\HDK\Core\Exceptions\Handler;
use Hyperf\Database\Exception\QueryException;
use Hyperf\Database\Model\ModelNotFoundException;
use Hyperf\Di\Annotation\Inject;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\Framework\Logger\StdoutLogger;
use Hyperf\HttpMessage\Exception\BadRequestHttpException;
use Hyperf\HttpMessage\Exception\HttpException;
use Hyperf\HttpMessage\Exception\MethodNotAllowedHttpException;
use Hyperf\HttpMessage\Exception\NotFoundHttpException;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Utils\Codec\Json;
use Hyperf\Validation\ValidationException;
use Lmc\HttpConstants\Header;
use Psr\Http\Message\ResponseInterface;
use RedisException;
use Singularity\HDK\Core\Constants\CommonErrorCode;
use Singularity\HDK\Core\Exceptions\ValidateException;
use Symfony\Component\Mailer\Exception\TransportException;
use Teapot\StatusCode\RFC\RFC7231;
use Throwable;
/**
* 通用异常处理
* Singularity\HDK\Utils\Exceptions\Handler\CommonHandler@hyperf-development-kit
*
* @author 李东云<dongyun.li@luxcreo.cn>
* Powered by PhpStorm
* Created on 2022/4/28
*/
class CommonHandler extends ExceptionHandler
{
/**
*
* @var RequestInterface|null
*/
#[Inject(required: false)]
private ?RequestInterface $request;
/**
* @var StdoutLogger
*/
#[Inject]
private StdoutLogger $logger;
/**
* {@inheritDoc}
*/
public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface
{
// 阻止异常冒泡
$this->stopPropagation();
$restful = config('common.response.restful');
$code_name = config('common.response.code_name');
$message_name = config('common.response.message_name');
$is_testing = config('app_status') === true;
$this->request?->url();
$is_debug = $this->request?->hasHeader('Postman-Token') || str_starts_with($this->request?->header('User-Agent'), 'apifox');
$error_type = $throwable::class;
$request_time = date('Y-m-d H:i:s');
$request_data = Json::encode($this->request?->getParsedBody());
$request_headers = Json::encode($this->request?->getHeaders());
// 901 程序语法错误
// 902 SQL 语法错误
if ($throwable instanceof QueryException) {
$code = match ($throwable->getCode()) {
'42S22' => CommonErrorCode::PROGRAM_SQL_COLUMN_NOT_FOUND,
default => CommonErrorCode::PROGRAM_SQL_ERROR,
};
$data = [$code_name => $code, $message_name => CommonErrorCode::getMessage($is_testing ? $code : CommonErrorCode::PROGRAM_SQL_ERROR)];
if ($is_testing) {
$data['details'] = ['sql' => $throwable->getSql(), 'error' => $throwable->getMessage()];
}
}
// 101 请求方式错误
if ($throwable instanceof MethodNotAllowedHttpException) {
$message = explode(': ', $throwable->getMessage());
$allow_method = explode(', ', $message[1]);
$code = CommonErrorCode::REQUEST_METHOD_ERROR;
$data = [$code_name => $code, $message_name => CommonErrorCode::getMessage($code, ['methods' => join(', ', $allow_method)]), 'currentMethod' => $this->request?->getMethod(), 'allowedMethod' => $allow_method];
}
// 验证失败
if ($throwable instanceof BadRequestHttpException) {
$data = [$code_name => CommonErrorCode::REQUEST_PARAMS_ERROR, $message_name => $is_testing ? $throwable->getMessage() : CommonErrorCode::getMessage(CommonErrorCode::SERVER_ERROR)];
}
if ($throwable instanceof ValidationException) {
$data = $throwable->validator->errors()->first();
if (is_numeric($data)) {
$code = (int) $data;
$data = CommonErrorCode::getMessage($code);
}
$data = [$code_name => $code ?? CommonErrorCode::REQUEST_PARAMS_ERROR, $message_name => $data];
}
if ($throwable instanceof ValidateException) {
$code = $throwable->getCode();
$message = $throwable->getMessage();
$data = [$code_name => $code, $message_name => empty($message) ? CommonErrorCode::getMessage($code, ['param' => $throwable->getFieldName()]) : $message];
if ($is_debug) {
$data['currentValue'] = $throwable->getCurrentValue();
$data['availableValue'] = $throwable->getAvailableValue();
}
}
// 路由不存在
if ($throwable instanceof NotFoundHttpException) {
$code = CommonErrorCode::ROUTE_NOT_FOUND;
$data = [$code_name => $code, $message_name => CommonErrorCode::getMessage($code)];
$status_code = 404;
}
// 模型不存在
if ($throwable instanceof ModelNotFoundException) {
$code = empty($throwable->getCode()) ? CommonErrorCode::MODEL_NOT_FOUND : $throwable->getCode();
$message = empty($throwable->getCode()) ? CommonErrorCode::getMessage($code, ['resource' => '资源']) : $throwable->getMessage();
$data = [$code_name => $code, $message_name => $message];
$status_code = 404;
}
// 300 服务出错
// 303 缓存异常
if ($throwable instanceof RedisException) {
$code = CommonErrorCode::SERVER_CACHE_REDIS_ERROR;
if ($throwable->getMessage() === 'Connection refused') {
$code = CommonErrorCode::SERVER_CACHE_REDIS_REFUSED_ERROR;
}
$data = [$code_name => $code, $message_name => CommonErrorCode::getMessage($is_testing ? $code : CommonErrorCode::SERVER_CACHE_REDIS_ERROR)];
}
// 306 消息异常
// 30601 邮箱发件异常
if ($throwable instanceof TransportException) {
$code = CommonErrorCode::SERVER_MESSAGE_EMAIL_ERROR;
if (strpos($throwable->getMessage(), '500 Error: bad syntax')) {
$code = CommonErrorCode::SERVER_MESSAGE_EMAIL_NOT_FOUND;
}
$data = [$code_name => $code, $message_name => CommonErrorCode::getMessage($is_testing ? $code : CommonErrorCode::SERVER_MESSAGE_EMAIL_ERROR)];
}
if (empty($data)) {
// 其他情况
$data = [$code_name => $is_testing ? $throwable->getCode() == 0 ? CommonErrorCode::SERVER_ERROR : $throwable->getCode() : CommonErrorCode::SERVER_ERROR, $message_name => $is_testing ? $throwable->getMessage() : __(CommonErrorCode::getMessage(CommonErrorCode::SERVER_ERROR))];
// 其他错误
if ($throwable instanceof HttpException) {
$data = [$code_name => $throwable->getCode() ?: $throwable->getStatusCode(), $message_name => $throwable->getMessage()];
}
}
$response = $response->withHeader(Header::CONTENT_TYPE, 'application/json; charset=utf-8');
if ($is_debug && $is_testing) {
$data['trace'] = ['errorType' => $error_type, 'errorTrack' => $throwable->getTrace()];
}
$cookies = json_encode($this->request->getCookieParams(), JSON_UNESCAPED_UNICODE);
$this->logger->error(
<<<ERROR_LOG
TYPE: {$error_type}
[{$data[$code_name]}] {$data[$message_name]}
{$throwable->getMessage()}
-------------------------------
REQUEST_TIME: {$request_time}
-------------------------------
REQUEST_HEADERS:
{$request_headers}
-------------------------------
REQUEST_COOKIES:
{$cookies}
-------------------------------
REQUEST_METHOD:
{$this->request?->getMethod()}
-------------------------------
REQUEST_URL:
{$this->request?->getUri()}
-------------------------------
REQUEST_QUERY:
{$this->request?->getQueryString()}
-------------------------------
REQUEST_DATA:
{$request_data}
-------------------------------
TRACE:
{$throwable->getTraceAsString()}
===============================
ERROR_LOG
);
$data = Json::encode($data);
if ($restful) {
$response = $response->withStatus($status_code ?? $throwable->status ?? $throwable->statusCode ?? RFC7231::INTERNAL_SERVER_ERROR);
}
return $response->withBody(new SwooleStream($data));
}
/**
* 判断该异常处理器是否要对该异常进行处理.
*/
public function isValid(Throwable $throwable): bool
{
return true;
}
}