如何处理客户端错误(如 Guzzle 或 Stripe PHP 客户端)
How to deal with clients errors (like Guzzle or Stripe PHP Client)
我发现自己又遇到了同样的问题:如何处理调用外部 APIs 的客户端?
问题是这样的
例如,如果我使用 Guzzle,它可能会抛出一些 exceptions . The same happens, for example, again, with the Stripe PHP client (here the documentation about errors)。
所以,问题总是一样的:对于我发出的每个调用,我都必须捕获异常并根据它们的类型采取行动。
一个典型的例子(取自Stripe的文档):
try {
// Use Stripe's library to make requests...
} catch(\Stripe\Error\Card $e) {
// Since it's a decline, \Stripe\Error\Card will be caught
$body = $e->getJsonBody();
$err = $body['error'];
print('Status is:' . $e->getHttpStatus() . "\n");
print('Type is:' . $err['type'] . "\n");
print('Code is:' . $err['code'] . "\n");
// param is '' in this case
print('Param is:' . $err['param'] . "\n");
print('Message is:' . $err['message'] . "\n");
} catch (\Stripe\Error\RateLimit $e) {
// Too many requests made to the API too quickly
} catch (\Stripe\Error\InvalidRequest $e) {
// Invalid parameters were supplied to Stripe's API
} catch (\Stripe\Error\Authentication $e) {
// Authentication with Stripe's API failed
// (maybe you changed API keys recently)
} catch (\Stripe\Error\ApiConnection $e) {
// Network communication with Stripe failed
} catch (\Stripe\Error\Base $e) {
// Display a very generic error to the user, and maybe send
// yourself an email
} catch (Exception $e) {
// Something else happened, completely unrelated to Stripe
}
因此,如果我对 API 进行 3 次调用,我必须重复此异常处理 3 次。
我可以更好地创建一个类似 handleException(\Exception $e)
的方法来管理异常:
/**
* Handles the Stripe's exceptions.
*
* @param \Exception $e
*/
private function handleException(\Exception $e)
{
/* Since it's a decline, \Stripe\Error\Card will be caught
$body = $e->getJsonBody();
$err = $body['error'];
print('Status is:' . $e->getHttpStatus() . "\n");
print('Type is:' . $err['type'] . "\n");
print('Code is:' . $err['code'] . "\n");
// param is '' in this case
print('Param is:' . $err['param'] . "\n");
print('Message is:' . $err['message'] . "\n");
*/
// Authentication with Stripe's API failed
if ($e instanceof \Stripe\Error\Authentication) {
// Immediately re-raise this exception to make the developer aware of the problem
throw $e;
}
elseif ($e instanceof \Stripe\Error\InvalidRequest) {
// This should never happen: if we are in development mode, we raise the exception, else we simply log it
die(dump($e));
}
elseif ($e instanceof \Stripe\Error\RateLimit) {
// Too many requests made to the API too quickly
die(dump('\Stripe\Error\Card error', $e));
}
elseif ($e instanceof \Stripe\Error\ApiConnection) {
// Network communication with Stripe failed
die(dump('\Stripe\Error\ApiConnection error', $e));
}
elseif ($e instanceof \Stripe\Error\Card) {
die(dump('\Stripe\Error\Card error', $e));
}
elseif ($e instanceof \Stripe\Error\Base) {
// Display a very generic error to the user, and maybe send
// yourself an email
die(dump('\Stripe\Error\Base error', $e));
}
elseif ($e instanceof \Exception) {
// Something else happened, completely unrelated to Stripe
die(dump('\Exception error', $e));
}
但是,问题又来了:如何处理错误?
分析各种异常:
Authentication
:我立即提出它,因为它一旦修复就不会再发生:开发人员只需检查访问密钥;
InvalidRequest
: 有问题,稍后查看;
rateLimit
:我应该实施某种指数退避 as suggested in the documentaion
Network communication
: 真不知道怎么模拟也不知道
Card
: 我现在还没有研究这个问题:我想先解决其他问题,尤其是第2点和第4点。
因此,请参阅 elseif (\Stripe\Error\InvalidRequest)
:如果出现此异常,我该怎么办?在我写的评论中,如果我处于开发模式,我可以引发异常,而如果我不是,我应该记录错误......但是,这是处理问题的正确方法吗?
所以,最终,由于我不太清楚如何继续讨论,我该如何处理这样的错误? xaples 与 Stripe Api 一起使用,但同样适用于 Guzzle 异常,以及许多其他使用异常的库。
有没有关于如何处理这种情况的指导?一些最佳实践?我可以从中获得灵感的一些例子?任何建议或正确的方向表示赞赏。谢谢。
Middleware 再见!!
这就是你要做的。
当你创建你的对象时,你也创建了一个中间件然后注入它。
$handler = new CurlHandler();
$stack = HandlerStack::create($handler);
$stack->push(ErrorHandlerMiddleware::create(), 'http_error');
那么您的 ErrorHandlerMiddleware 可能如下所示:
class ErrorHandlerMiddleware {
public static function create() {
return function (callable $handler) {
return function ($request, array $options) use ($handler {
return $handler($request, $options)->then(
function (ResponseInterface $response) use ($request, $handler) {
$code = $response->getStatusCode();
if ($code < 400) {
return $response;
}
$response_body = json_decode($response->getBody(), true);
switch ($code) {
case 400:
// do what you need
case 404:
// do what you need
case 500:
// do what you need
}
},
function (RequestException $e) use ($request, $handler) {
throw new Exceptions\ConnectException('Service Unavailable - Connection Errors '. $e->getMessage(), $request);
}
);
};
};
}
}
现在您不必重复自己。
PS:当我说 "do what you need" 时,我的意思是抛出您的自定义异常或您需要做的任何事情
我发现自己又遇到了同样的问题:如何处理调用外部 APIs 的客户端?
问题是这样的
例如,如果我使用 Guzzle,它可能会抛出一些 exceptions . The same happens, for example, again, with the Stripe PHP client (here the documentation about errors)。
所以,问题总是一样的:对于我发出的每个调用,我都必须捕获异常并根据它们的类型采取行动。
一个典型的例子(取自Stripe的文档):
try {
// Use Stripe's library to make requests...
} catch(\Stripe\Error\Card $e) {
// Since it's a decline, \Stripe\Error\Card will be caught
$body = $e->getJsonBody();
$err = $body['error'];
print('Status is:' . $e->getHttpStatus() . "\n");
print('Type is:' . $err['type'] . "\n");
print('Code is:' . $err['code'] . "\n");
// param is '' in this case
print('Param is:' . $err['param'] . "\n");
print('Message is:' . $err['message'] . "\n");
} catch (\Stripe\Error\RateLimit $e) {
// Too many requests made to the API too quickly
} catch (\Stripe\Error\InvalidRequest $e) {
// Invalid parameters were supplied to Stripe's API
} catch (\Stripe\Error\Authentication $e) {
// Authentication with Stripe's API failed
// (maybe you changed API keys recently)
} catch (\Stripe\Error\ApiConnection $e) {
// Network communication with Stripe failed
} catch (\Stripe\Error\Base $e) {
// Display a very generic error to the user, and maybe send
// yourself an email
} catch (Exception $e) {
// Something else happened, completely unrelated to Stripe
}
因此,如果我对 API 进行 3 次调用,我必须重复此异常处理 3 次。
我可以更好地创建一个类似 handleException(\Exception $e)
的方法来管理异常:
/**
* Handles the Stripe's exceptions.
*
* @param \Exception $e
*/
private function handleException(\Exception $e)
{
/* Since it's a decline, \Stripe\Error\Card will be caught
$body = $e->getJsonBody();
$err = $body['error'];
print('Status is:' . $e->getHttpStatus() . "\n");
print('Type is:' . $err['type'] . "\n");
print('Code is:' . $err['code'] . "\n");
// param is '' in this case
print('Param is:' . $err['param'] . "\n");
print('Message is:' . $err['message'] . "\n");
*/
// Authentication with Stripe's API failed
if ($e instanceof \Stripe\Error\Authentication) {
// Immediately re-raise this exception to make the developer aware of the problem
throw $e;
}
elseif ($e instanceof \Stripe\Error\InvalidRequest) {
// This should never happen: if we are in development mode, we raise the exception, else we simply log it
die(dump($e));
}
elseif ($e instanceof \Stripe\Error\RateLimit) {
// Too many requests made to the API too quickly
die(dump('\Stripe\Error\Card error', $e));
}
elseif ($e instanceof \Stripe\Error\ApiConnection) {
// Network communication with Stripe failed
die(dump('\Stripe\Error\ApiConnection error', $e));
}
elseif ($e instanceof \Stripe\Error\Card) {
die(dump('\Stripe\Error\Card error', $e));
}
elseif ($e instanceof \Stripe\Error\Base) {
// Display a very generic error to the user, and maybe send
// yourself an email
die(dump('\Stripe\Error\Base error', $e));
}
elseif ($e instanceof \Exception) {
// Something else happened, completely unrelated to Stripe
die(dump('\Exception error', $e));
}
但是,问题又来了:如何处理错误?
分析各种异常:
Authentication
:我立即提出它,因为它一旦修复就不会再发生:开发人员只需检查访问密钥;InvalidRequest
: 有问题,稍后查看;rateLimit
:我应该实施某种指数退避 as suggested in the documentaionNetwork communication
: 真不知道怎么模拟也不知道Card
: 我现在还没有研究这个问题:我想先解决其他问题,尤其是第2点和第4点。
因此,请参阅 elseif (\Stripe\Error\InvalidRequest)
:如果出现此异常,我该怎么办?在我写的评论中,如果我处于开发模式,我可以引发异常,而如果我不是,我应该记录错误......但是,这是处理问题的正确方法吗?
所以,最终,由于我不太清楚如何继续讨论,我该如何处理这样的错误? xaples 与 Stripe Api 一起使用,但同样适用于 Guzzle 异常,以及许多其他使用异常的库。
有没有关于如何处理这种情况的指导?一些最佳实践?我可以从中获得灵感的一些例子?任何建议或正确的方向表示赞赏。谢谢。
Middleware 再见!!
这就是你要做的。
当你创建你的对象时,你也创建了一个中间件然后注入它。
$handler = new CurlHandler();
$stack = HandlerStack::create($handler);
$stack->push(ErrorHandlerMiddleware::create(), 'http_error');
那么您的 ErrorHandlerMiddleware 可能如下所示:
class ErrorHandlerMiddleware {
public static function create() {
return function (callable $handler) {
return function ($request, array $options) use ($handler {
return $handler($request, $options)->then(
function (ResponseInterface $response) use ($request, $handler) {
$code = $response->getStatusCode();
if ($code < 400) {
return $response;
}
$response_body = json_decode($response->getBody(), true);
switch ($code) {
case 400:
// do what you need
case 404:
// do what you need
case 500:
// do what you need
}
},
function (RequestException $e) use ($request, $handler) {
throw new Exceptions\ConnectException('Service Unavailable - Connection Errors '. $e->getMessage(), $request);
}
);
};
};
}
}
现在您不必重复自己。
PS:当我说 "do what you need" 时,我的意思是抛出您的自定义异常或您需要做的任何事情