【问题标题】:How to deal with clients errors (like Guzzle or Stripe PHP Client)如何处理客户端错误(如 Guzzle 或 Stripe PHP 客户端)
【发布时间】:2017-02-12 17:26:55
【问题描述】:

我发现自己又遇到了同样的问题:如何处理调用外部 API 的客户端?

问题是这样的。

如果我使用,例如,Guzzle 可能会抛出一些 exceptions 。例如,Stripe PHP 客户端(这里是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));
    }

但是,问题又是:如何处理错误?

分析各种异常:

  1. Authentication:我马上提出来,因为它一旦修复就不会再发生了:开发者必须简单地检查访问密钥;
  2. InvalidRequest: 有问题,后面看;
  3. rateLimit:我应该实现某种指数退避as suggested in the documentaion
  4. Network communication:我真的不知道该怎么做也不知道怎么模拟
  5. Card:我现在还没研究这个问题:我想先解决其他问题,特别是第2点和第4点。

那么,请参阅elseif (\Stripe\Error\InvalidRequest):如果引发此异常,我该怎么办?在我写的评论中,如果我处于开发模式,我可以引发异常,而如果我不是,我应该记录错误......但是,这是处理问题的正确方法吗?

所以,最终,由于我不知道如何继续讨论,我该如何处理这样的错误? xaples 与 Stripe Api 一起使用,但同样适用于 Guzzle 异常以及许多其他使用异常的库。

是否有一些关于如何处理这种情况的指导?一些最佳实践?我可以从中获得灵感的一些例子?任何建议或正确的方向表示赞赏。谢谢。

【问题讨论】:

  • 你知道 Symfony 的自定义异常监听器功能吗? symfony.com/doc/current/event_dispatcher.html 至少这可以让你在一个地方处理更深奥的异常。
  • 是的,好的,但问题是我不知道捕获异常后该怎么做,而不是如何捕获它们。如您所见,如果出现RateLmit 异常,我必须重试调用,但如果我捕获到InvalidRequest 异常,我该怎么办?
  • 因为 InvalidRequest 不应该在生产中发生,所以是的,记录它并确保立即通知某人有问题。但我理解这些问题。我只能建议看看一些现有的条带包是如何处理它的。
  • 现有的 Stripe 捆绑包 1) 非常旧或 2) 根本不处理问题 3) 集成在更大的捆绑包或某种一次性的巨型网关中,所以它们没用或学习起来太复杂:(因此我正在开发自己的...

标签: php symfony stripe-payments


【解决方案1】:

MiddlewareFTW!!

这就是你要做的。

当你创建你的对象时,你也创建了一个中间件然后注入它。

$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:当我说“做你需要的”时,我的意思是抛出你的自定义异常或你需要做的任何事情

【讨论】:

    猜你喜欢
    • 2018-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多