This commit is contained in:
Aether
2025-09-26 15:36:09 +08:00
parent 0c7142ad46
commit f3a672b5c9
6 changed files with 295 additions and 46 deletions

View File

@@ -4,38 +4,105 @@ declare(strict_types=1);
namespace Aether;
use Hyperf\Context\ApplicationContext;
use Hyperf\Context\Context;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\Rpc\Protocol;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Http\Message\ResponseInterface;
use Throwable;
use function Hyperf\Support\env;
class RpcExceptionHandler extends ExceptionHandler
{
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface
{
// 微服务间调用返回更精简的错误信息
$data = [
'code' => $throwable->getCode() ?: 500,
'message' => $throwable->getMessage() ?: '服务调用失败',
'request_id' => Context::get('request_id', ''),
];
try {
// 获取请求ID用于日志追踪
$requestId = Context::get('request_id', '');
$protocol = ApplicationContext::getContainer()->get(Protocol::class);
$response->getBody()->write($protocol->pack($data));
// 从请求中获取可能的RPC ID
$rpcId = $this->getRpcIdFromRequest();
return $response;
// 构建符合JSON-RPC 2.0规范的错误响应
$errorResponse = [
'jsonrpc' => '2.0',
'id' => $rpcId ?? $requestId,
'error' => [
'code' => $throwable->getCode() ?: -32603, // 默认服务器错误码
'message' => $throwable->getMessage() ?: 'Internal error',
'data' => [
'request_id' => $requestId,
'exception_type' => get_class($throwable),
// 开发环境下可以添加更多调试信息
'debug' => env('APP_ENV') === 'dev' ? [
'file' => $throwable->getFile(),
'line' => $throwable->getLine(),
] : null,
],
],
];
// JSON编码
$jsonResponse = json_encode($errorResponse, JSON_UNESCAPED_UNICODE);
// 检查JSON编码错误
if (json_last_error() !== JSON_ERROR_NONE) {
$jsonResponse = json_encode([
'jsonrpc' => '2.0',
'id' => $rpcId ?? $requestId,
'error' => [
'code' => -32603,
'message' => 'Failed to encode error response',
'data' => ['request_id' => $requestId],
],
]);
}
return $response
->withHeader('Content-Type', 'application/json')
->withBody(new SwooleStream((string) $jsonResponse));
} catch (Throwable $e) {
$fallbackResponse = json_encode([
'jsonrpc' => '2.0',
'id' => null,
'error' => [
'code' => -32603,
'message' => 'Fatal error occurred in exception handler',
'data' => ['original_error' => $throwable->getMessage()],
],
]);
return $response
->withHeader('Content-Type', 'application/json')
->withBody(new SwooleStream((string) $fallbackResponse));
}
}
public function isValid(Throwable $throwable): bool
{
return true;
}
/**
* 尝试从请求中获取RPC ID.
*/
private function getRpcIdFromRequest(): mixed
{
try {
$request = Context::get('hyperf.request');
if ($request) {
$body = $request->getParsedBody();
if (is_array($body) && isset($body['id'])) {
return $body['id'];
}
}
} catch (Throwable $e) {
// 获取失败时静默处理
}
return null;
}
}