处理Guzzle异常并获取HTTP正文


122

当服务器返回4xx和5xx状态代码时,我想处理来自Guzzle的错误。我发出这样的请求:

$client = $this->getGuzzleClient();
$request = $client->post($url, $headers, $value);
try {
    $response = $request->send();
    return $response->getBody();
} catch (\Exception $e) {
    // How can I get the response body?
}

$e->getMessage返回代码信息,但不返回HTTP响应的主体。如何获得响应主体?


1
这个问题与这个问题stackoverflow.com/questions/17658283/…相关,那里的答案也可能有帮助。
Trendfischer

Answers:


84

食尸鬼3.x

根据文档,您可以捕获适当的异常类型(ClientErrorResponseException针对4xx错误)并调用其getResponse()方法以获取响应对象,然后对其进行调用getBody()

use Guzzle\Http\Exception\ClientErrorResponseException;

...

try {
    $response = $request->send();
} catch (ClientErrorResponseException $exception) {
    $responseBody = $exception->getResponse()->getBody(true);
}

传递true给该getBody函数表示您要以字符串形式获取响应正文。否则,您将获得它作为class的实例Guzzle\Http\EntityBody


232

食尸鬼6.x

根据文档,您可能需要捕获的异常类型为:

  • GuzzleHttp\Exception\ClientException 对于400级错误
  • GuzzleHttp\Exception\ServerException 用于500级错误
  • GuzzleHttp\Exception\BadResponseException 对于两者(这是他们的超类)

因此,用于处理此类错误的代码如下所示:

$client = new GuzzleHttp\Client;
try {
    $client->get('http://google.com/nosuchpage');    
}
catch (GuzzleHttp\Exception\ClientException $e) {
    $response = $e->getResponse();
    $responseBodyAsString = $response->getBody()->getContents();
}

12
对我来说,$response->getBody()->getContents()将返回一个空字符串。然后,我在文档中偶然发现了这一点: \GuzzleHttp\Psr7\str($e->getResponse()) 将响应转换为Psr7字符串后,得到了格式正确且完整的错误消息。
安迪广场

3
@AndyPlace看完PSR 7之后(我写这个答案的时候,我链接到的文档部分没有引用,但是现在),这对我来说并不立刻明白为什么调用Psr7\str()会产生不同的结果到->getContents()。您是否有一个最小的示例来说明这一点,可能会让我理解这一点,也许还会更新此答案?
Mark Amery

24
值得一提的是,'http_errors' => false可以在Guzzle请求中传递该选项,以禁止抛出异常。然后,$response->getBody()无论状态码是什么,都可以获取主体,并且可以根据需要测试状态码$response->getStatusCode()
tremby

2
作为@AndyPlace,$response->getBody()->getContents()在一种情况下给我一个空字符串,我不明白为什么。但是using使用\GuzzleHttp\Psr7\str()字符串形式返回所有HTTP响应,而我只返回HTTP正文。如文档中所述,可以通过将主体转换为字符串来使用主体。$stringBody = (string) $clientException->getResponse()->getBody();
AnthonyB

1
这为我做到了,尽管我得到的\GuzzleHttp\Exception\RequestException却是返回400状态码的。尝试{$ request-> api('POST','endpoint.json'); } catch(RequestException $ e){print_r($ e-> getResponse()-> getBody()-> getContents()); }
jpcaparas

54

尽管以上答案很好,但它们不会捕获网络错误。正如Mark提到的,BadResponseException只是ClientException和ServerException的超类。但是RequestException还是BadResponseException的超类。RequestException不仅会引发400和500个错误,还会引发网络错误和无限重定向。因此,假设您请求下面的页面,但是您的网络正在播放,并且您的捕获只期待BadResponseException。那么您的应用程序将抛出错误。

在这种情况下最好期待RequestException并检查响应。

try {
  $client->get('http://123123123.com')
} catch (RequestException $e) {

  // If there are network errors, we need to ensure the application doesn't crash.
  // if $e->hasResponse is not null we can attempt to get the message
  // Otherwise, we'll just pass a network unavailable message.
  if ($e->hasResponse()) {
    $exception = (string) $e->getResponse()->getBody();
    $exception = json_decode($exception);
    return new JsonResponse($exception, $e->getCode());
  } else {
    return new JsonResponse($e->getMessage(), 503);
  }

}

JsonResponseGuzzle的课程吗?
aexl

JsonResponse来自Symfony的
第一章

14

截至2019年,这是我从上面的答案和Guzzle文档中阐述的内容,以处理异常,获取响应正文,状态代码,消息以及其他有时有价值的响应项目。

try {
    /**
     * We use Guzzle to make an HTTP request somewhere in the
     * following theMethodMayThrowException().
     */
    $result = theMethodMayThrowException();
} catch (\GuzzleHttp\Exception\RequestException $e) {
    /**
     * Here we actually catch the instance of GuzzleHttp\Psr7\Response
     * (find it in ./vendor/guzzlehttp/psr7/src/Response.php) with all
     * its own and its 'Message' trait's methods. See more explanations below.
     *
     * So you can have: HTTP status code, message, headers and body.
     * Just check the exception object has the response before.
     */
    if ($e->hasResponse()) {
        $response = $e->getResponse();
        var_dump($response->getStatusCode()); // HTTP status code;
        var_dump($response->getReasonPhrase()); // Response message;
        var_dump((string) $response->getBody()); // Body, normally it is JSON;
        var_dump(json_decode((string) $response->getBody())); // Body as the decoded JSON;
        var_dump($response->getHeaders()); // Headers array;
        var_dump($response->hasHeader('Content-Type')); // Is the header presented?
        var_dump($response->getHeader('Content-Type')[0]); // Concrete header value;
    }
}
// process $result etc. ...

瞧 您可以在方便分隔的项目中获得响应的信息。

注意事项:

With catch子句\Exception随着Guzzle自定义异常对其的扩展,捕获了继承链PHP根异常类 。

对于在幕后使用Guzzle的用例(例如在Laravel或AWS API PHP SDK中)使用这种方法,因此您无法捕获真正的Guzzle异常,这种方法可能有用。

在这种情况下,异常类可能不是Guzzle文档中提到的那个(例如,GuzzleHttp\Exception\RequestException作为Guzzle的根异常)。

因此,您必须抓住\Exception它,但请记住,它仍然是Guzzle异常类实例。

虽然小心使用。这些包装器可能会使Guzzle $e->getResponse()对象的真正方法不可用。在这种情况下,您必须查看包装程序的实际异常源代码,并找出如何获取状态,消息等,而不是使用Guzzle $response的方法。

如果您自己直接致电Guzzle,则可以捕获GuzzleHttp\Exception\RequestException或使用案例条件提及他们的例外文档中提到的其他任何人。


1
$response除非已检查$e->hasResponse(),否则不应在处理异常时在对象上调用方法,否则$response可能null会导致致命错误。
pwaring

@pwaring,是的。就像Guzzle例外文档所说的那样。更新了答案。谢谢。
Valentine Shi

1
...但是在修复之后这仍然是有问题的。您正在捕获所有异常,而不仅仅是Guzzle异常,然后您调用$e->hasResponse结果,该方法对于非Guzzle异常当然是不存在的。因此,如果您从引发了一个非Guzzle异常theMethodMayThrowException(),则此代码将捕获该异常,尝试调用一个不存在的方法,并由于该不存在的方法而崩溃,从而有效地隐藏了错误的真正原因。最好是抓住GuzzleHttp\Exception\RequestException而不是Exception避免这种情况。
Mark Amery

1
@MarkAmery,您的观点完全正确。谢谢。我更新了答案正文。
情人史

1
@JaberAlNahian很高兴听到:)那是我的意图。随时欢迎。
情人石

4

如果放置'http_errors' => false在耗时的请求选项中,则会在出现4xx或5xx错误时停止抛出异常,如下所示:$client->get(url, ['http_errors' => false])。然后解析响应,无论是正常还是错误,都将在响应中 获取更多信息


这个问题是关于句柄错误而不是要求停止错误异常
Dlk
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.