Files
hyperf_data/extend/Aether/PHP/Hyperf/Exception/AetherExceptionHandler.php
2025-09-25 09:12:22 +08:00

144 lines
4.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
declare(strict_types=1);
namespace Aether\Exception;
use Aether\AetherValidator;
use Hyperf\Context\Context;
use Hyperf\Contract\StdoutLoggerInterface; // 引入模型未找到异常
use Hyperf\Database\Model\ModelNotFoundException;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\Validation\ValidationException;
use Psr\Http\Message\ResponseInterface;
use Throwable;
use function Hyperf\Support\env;
class AetherExceptionHandler extends ExceptionHandler
{
public function __construct(protected StdoutLoggerInterface $logger)
{
}
public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface
{
$requestId = Context::get('request_id', uniqid());
$result = $this->formatErrorResponse($throwable, $requestId);
// 记录错误日志(包含完整堆栈)
$this->logger->error(sprintf(
'Exception [%s] | RequestId: %s | Message: %s in %s:%d',
get_class($throwable),
$requestId,
$throwable->getMessage(),
$throwable->getFile(),
$throwable->getLine()
));
if (env('APP_ENV') === 'dev') {
$this->logger->error($throwable->getTraceAsString());
}
// 确保状态码合法100-599
$statusCode = $result['code'] ?? 500;
if ($statusCode < 100 || $statusCode >= 600) {
$statusCode = 500;
}
// 构建响应
return $response
->withHeader('Content-Type', 'application/json')
->withStatus($statusCode)
->withBody(new SwooleStream(json_encode($result, JSON_UNESCAPED_UNICODE)));
}
public function isValid(Throwable $throwable): bool
{
return true;
}
private function formatErrorResponse(Throwable $throwable, string $requestId): array
{
// 模型未找到异常
if ($throwable instanceof ModelNotFoundException) {
return [
'code' => 404, // 资源不存在标准状态码
'message' => $throwable->getMessage() ?: '请求的资源不存在',
'data' => env('APP_ENV') === 'dev' ? [
'file' => $throwable->getFile(),
'line' => $throwable->getLine(),
] : null,
'request_id' => $requestId,
'timestamp' => time(),
];
}
// 自定义验证异常
if ($throwable instanceof ValidationFailedException) {
return $this->formatValidationError($throwable, $requestId);
}
// 原生验证异常
if ($throwable instanceof ValidationException) {
return $this->formatNativeValidationError($throwable, $requestId);
}
// 其他异常如RuntimeException等
return [
'code' => 600,
'message' => env('APP_ENV') === 'dev' ? $throwable->getMessage() : '服务暂时不可用',
'data' => env('APP_ENV') === 'dev' ? [
'file' => $throwable->getFile(),
'line' => $throwable->getLine(),
'trace' => explode("\n", $throwable->getTraceAsString()),
] : null,
'request_id' => $requestId,
'timestamp' => time(),
];
}
private function formatValidationError(ValidationFailedException $e, string $requestId): array
{
$validatorInstance = new class extends AetherValidator {
protected function scenes(): array
{
return [];
}
};
return [
'code' => 422,
'message' => $e->getMessage(),
'data' => [
'errors' => $validatorInstance->formatValidationErrors($e->validator),
'scene' => $e->getScene(),
'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null,
],
'request_id' => $requestId,
'timestamp' => time(),
];
}
private function formatNativeValidationError(ValidationException $e, string $requestId): array
{
$validatorInstance = new class extends AetherValidator {
protected function scenes(): array
{
return [];
}
};
return [
'code' => 422,
'message' => '参数验证失败',
'data' => [
'errors' => $validatorInstance->formatValidationErrors($e->validator),
'validated_data' => env('APP_ENV') === 'dev' ? $e->validator->getData() : null,
],
'request_id' => $requestId,
'timestamp' => time(),
];
}
}