Commit 66e213ae authored by 谢宇轩's avatar 谢宇轩

refactor: 重构错误处理

parent 3dfc9861
......@@ -7,11 +7,12 @@ use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Exception\TransferException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Request;
use InvalidArgumentException;
use Jiwei\EasyHttpSdk\Exception\ApplicationException;
use Jiwei\EasyHttpSdk\Exception\GuiltyResultException;
use Jiwei\EasyHttpSdk\Exception\SdkException;
use Jiwei\EasyHttpSdk\Exception\TimeOutExcetpion;
use Jiwei\EasyHttpSdk\Exception\UnknowResultException;
use Jiwei\EasyHttpSdk\Http\SdkRequest;
use Jiwei\EasyHttpSdk\Middleware\JwtMiddleware;
use Psr\Cache\CacheItemPoolInterface;
......@@ -29,14 +30,12 @@ class Application implements ClientInterface
private const TOKEN_CACHE_KEY = "Auth.%s";
private const EXPIRES_AT = 30000;
/** @var string jwtToken */
private $jwtToken = "";
/** @var Option $option */
private $option;
/** @var string jwtToken */
private $jwtToken = "";
/** @var null|LoggerInterface 日志记录器 */
protected $logger = null;
......@@ -194,6 +193,7 @@ class Application implements ClientInterface
public function resultDecode(SdkRequest $request, ResponseInterface $response): array
{
// context 是没有处理前的上下文
$context = $this->lastRequestContext;
if ($context['request']) {
......@@ -219,31 +219,28 @@ class Application implements ClientInterface
$rpcResult = [];
if ($response->getStatusCode() != 304) {
$responseInfo = $response->getBody()->getContents();
$rpcResult = json_decode($responseInfo, true);
if (json_last_error()) {
if ($this->logger) {
$this->logger->error("sdk error, bad response content.", $context);
}
throw new ApplicationException("Content Format error.", $request, []);
}
}
$errorName = $rpcResult['name'] ?? "";
// Paypal的API错误结果是通用的,所以这里简单处理一下
if (!empty($errorName) && $response->getStatusCode() >= 400) {
$errorMessage = sprintf("%s : %s", $errorName, $rpcResult['message'] ?? "");
$this->lastRequestContext['error'] = $errorMessage;
$this->lastRequestContext['error_detail'] = $rpcResult;
try {
$rpcResult = $this->option->errorHandlingPolicy()->process($response);
} catch (GuiltyResultException $exception) {
// 没有通过预期判定,转化为 SDK Exception
$this->lastRequestContext['error_message'] = $exception->getMessage();
$this->lastRequestContext['error_detail'] = $exception->getOptions(); // 获取错误的详情
if ($this->logger) {
$this->logger->warning("Error", $this->lastRequestContext);
}
throw new SdkException($errorMessage, $request, $response, $rpcResult);
throw new SdkException($exception->getMessage(), $request, $response, $rpcResult);
} catch (UnknowResultException $exception) {
// 无法解析,转化为 Application Exception
// 日志中保留 OUT PUT
$this->lastRequestContext['error_message'] = $exception->getMessage();
$this->lastRequestContext['error_detail'] = $exception->getResult(); // 获取错误的详情
if ($this->logger) {
$this->logger->error("sdk error, bad response content.", $this->lastRequestContext);
}
throw new ApplicationException("Content Format error.", $request, []);
}
$version = $response->getHeader('ETag');
if (!empty($version)) {
$rpcResult = array_merge($rpcResult, [
'version' => $version[0]
......@@ -252,8 +249,6 @@ class Application implements ClientInterface
if ($this->logger) {
$this->logger->info("Success", $context);
}
return $rpcResult;
}
......@@ -286,7 +281,7 @@ class Application implements ClientInterface
if (!$currentToken->isHit()) {
$token = $this->getAccessToken($app_key, $app_secret);
$currentToken->set($token)->expiresAfter(self::EXPIRES_AT);
$currentToken->set($token)->expiresAfter($this->option->getAuthExpires());
$this->cache->save($currentToken);
}
......@@ -303,7 +298,7 @@ class Application implements ClientInterface
private function getAccessToken(string $app_key, string $app_secret): string
{
try {
$request = $this->option->getAuthMoudel()($app_key, $app_secret);
$request = $this->option->authorization()($app_key, $app_secret);
$response = $this->client->send($request);
} catch (TransferException $exception) {
throw new RuntimeException('time out!');
......
<?php
namespace Jiwei\EasyHttpSdk\Exception;
use Throwable;
use \UnexpectedValueException;
class GuiltyResultException extends UnexpectedValueException
{
/** @var array<string, mixed> */
private $options;
/**
* @param string $message
* @param array<string, mixed> $options
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($message = "", $options = [], $code = 0, Throwable $previous = null)
{
$this->options = $options;
parent::__construct($message, $code, $previous);
}
/**
* @return array<string, mixed>
*/
public function getOptions(): array
{
return $this->options;
}
}
<?php
namespace Jiwei\EasyHttpSdk\Exception;
use Throwable;
use \UnexpectedValueException;
class UnknowResultException extends UnexpectedValueException
{
/** @var string */
public $result;
/**
* @param string $message
* @param string $result
* @param int $code
* @param Throwable|null $previous
*/
public function __construct($message = "", $result = "", $code = 0, Throwable $previous = null)
{
$this->result = $result;
parent::__construct($message, $code, $previous);
}
/**
* @return string
*/
public function getResult(): string
{
return $this->result;
}
}
<?php
declare(strict_types=1);
namespace Jiwei\EasyHttpSdk\Http;
use ArrayAccess;
use GuzzleHttp\Psr7\Response;
use Jiwei\EasyHttpSdk\Exception\SdkException;
/**
* @implements ArrayAccess<string, mixed>
*/
class Context implements ArrayAccess
{
/** @var SdkRequest */
public $request;
/** @var Response */
public $response;
/** @var int */
public $requestId;
/** @var string */
public $endpoint;
/** @var string */
public $action;
/** @var string */
public $stage;
/** @var string */
public $expend;
/** @var string */
public $httpStatus;
/** @var array<string, mixed> */
public $data;
/** @var SdkException */
public $exception;
/** @var string */
public $errorMessage;
/** @var array<string, mixed>|string */
public $errorDetail;
/**
* @param $offset
* @return bool
*/
public function offsetExists($offset): bool
{
return isset($this->$offset);
}
/**
* @param $offset
* @return mixed
*/
public function offsetGet($offset)
{
return $this->$offset ?? null;
}
/**
* @param $offset
* @param $value
* @return void
*/
public function offsetSet($offset, $value)
{
if (in_array($offset, get_object_vars($this))) {
$this->$offset = $value;
}
}
/**
* @param $offset
* @return void
*/
public function offsetUnset($offset)
{
$this->$offset = null;
}
}
<?php
declare(strict_types=1);
namespace Jiwei\EasyHttpSdk\Middleware;
......@@ -39,4 +40,4 @@ class EtagMiddleware implements MiddlewareInterface
return $handler($request, $options);
};
}
}
\ No newline at end of file
}
<?php
declare(strict_types=1);
namespace Jiwei\EasyHttpSdk\Middleware;
......@@ -29,4 +30,4 @@ class JwtMiddleware implements MiddlewareInterface
return $handler($request, $options);
};
}
}
\ No newline at end of file
}
<?php
declare(strict_types=1);
namespace Jiwei\EasyHttpSdk\Middleware;
interface MiddlewareInterface
{
}
\ No newline at end of file
}
<?php
declare(strict_types=1);
namespace Jiwei\EasyHttpSdk\Middleware;
......@@ -36,4 +37,4 @@ class RequestIDMiddleware implements MiddlewareInterface
return $handler($request, $options);
};
}
}
\ No newline at end of file
}
<?php
declare(strict_types=1);
namespace Jiwei\EasyHttpSdk;
use GuzzleHttp\Psr7\Request;
use http\Exception\InvalidArgumentException;
use Jiwei\EasyHttpSdk\Policy\HandlingPolicyInterface;
abstract class Option
{
......@@ -12,39 +12,49 @@ abstract class Option
'development' => '',
'production' => ''
];
const AUTH_CACHE_EXPIRES_AT = 30000;
/** @var string App Key 应用标志 */
private $app_id;
private $appId;
/** @var string App Secret 应用密钥 */
private $app_secret;
private $appSecret;
/** @var string SDK 的 Stage 环境 */
private $stage = "development";
/** @var float 超时时间 */
private $time_out = 3.0;
private $timeOut = 3.0;
/** @var bool 调试模式 */
private $debug = false;
/**
* @return \Closure
*/
abstract public function getAuthMoudel(): \Closure;
abstract public function authorization(): \Closure;
/**
* 错误处理策略
* @return HandlingPolicyInterface
*/
abstract public function errorHandlingPolicy(): HandlingPolicyInterface;
/**
* @param string $app_id
* @param string $appId
* @return Option
*/
public function setAppId(string $app_id): self
public function setAppId(string $appId): self
{
$this->app_id = $app_id;
$this->appId = $appId;
return $this;
}
/**
* @param string $app_secret
* @param string $appSecret
* @return Option
*/
public function setAppSecret(string $app_secret): self
public function setAppSecret(string $appSecret): self
{
$this->app_secret = $app_secret;
$this->appSecret = $appSecret;
return $this;
}
......@@ -59,12 +69,12 @@ abstract class Option
}
/**
* @param float $time_out
* @param float $timeOut
* @return Option
*/
public function setTimeout(float $time_out): self
public function setTimeout(float $timeOut): self
{
$this->time_out = $time_out;
$this->timeOut = $timeOut;
return $this;
}
......@@ -83,7 +93,7 @@ abstract class Option
*/
public function getAppSecret(): string
{
return $this->app_secret;
return $this->appSecret;
}
/**
......@@ -95,6 +105,15 @@ abstract class Option
return static::ENDPONIT_HOSTS[$this->getStage()] ?? "";
}
/**
* @return int
*/
public function getAuthExpires(): int
{
return static::AUTH_CACHE_EXPIRES_AT ?? 30000;
}
/**
* @return string
*/
......@@ -108,7 +127,7 @@ abstract class Option
*/
public function getTimeOut(): float
{
return $this->time_out;
return $this->timeOut;
}
/**
......@@ -116,7 +135,7 @@ abstract class Option
*/
public function getAppId(): string
{
return $this->app_id;
return $this->appId;
}
/**
......
<?php
namespace Jiwei\EasyHttpSdk\Policy;
use Jiwei\EasyHttpSdk\Exception\GuiltyResultException;
use Jiwei\EasyHttpSdk\Exception\UnknowResultException;
use Psr\Http\Message\ResponseInterface;
class DefaultErrorHandlingPolicy implements HandlingPolicyInterface
{
/**
* @param ResponseInterface $response
* @return array<string, mixed>
*/
public function process(ResponseInterface $response): array
{
if ($response->getStatusCode() == 304) {
return [];
}
$responseInfo = $response->getBody()->getContents();
$rpcResult = json_decode($responseInfo, true);
if (json_last_error()) {
throw new UnknowResultException("Content Format error.", $responseInfo);
}
if (!empty($rpcResult['name']) && $response->getStatusCode() >= 400) {
$errorMessage = sprintf("%s : %s", $rpcResult['name'], $rpcResult['message'] ?? "");
throw new GuiltyResultException($errorMessage, $rpcResult);
}
return $rpcResult;
}
}
<?php
namespace Jiwei\EasyHttpSdk\Policy;
use Psr\Http\Message\ResponseInterface;
interface HandlingPolicyInterface
{
/**
* @param ResponseInterface $response
* @return array<string, mixed>
*/
public function process(ResponseInterface $response): array;
}
<?php
declare(strict_types=1);
namespace Jiwei\EasyHttpSdk;
use GuzzleHttp\Psr7\Request;
use Jiwei\EasyHttpSdk\Policy\DefaultErrorHandlingPolicy;
use Jiwei\EasyHttpSdk\Policy\HandlingPolicyInterface;
class XXXSDKOption extends Option
{
private const AUTH_API_ROUTE = "/api/access/token";
const AUTH_CACHE_EXPIRES_AT = 30000;
const ENDPONIT_HOSTS = [
'local' => 'localhost:8080',
'development' => 'localhost:8080',
'production' => 'localhost:8080'
];
public function getAuthMoudel(): \Closure
/**
* 鉴权模式
*
* @return \Closure
*/
public function authorization(): \Closure
{
return function (string $appId, string $appSecret): Request {
return new Request('post', self::AUTH_API_ROUTE, [
......@@ -24,6 +34,14 @@ class XXXSDKOption extends Option
])
]);
};
}
/**
* 错误处理策略
* @return HandlingPolicyInterface
*/
public function errorHandlingPolicy(): HandlingPolicyInterface
{
return new DefaultErrorHandlingPolicy();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment