处理Guzzle异常并获取HTTP正文

时间:2013-11-02 23:29:52

标签: php guzzle

当服务器返回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响应的正文。我怎样才能获得回复正文?

9 个答案:

答案 0 :(得分:180)

Guzzle 6.x

Per the docs,您可能需要捕获的异常类型是:

  • dc.barChart("#test") .width(200).height(200) .dimension({}) .x(d3.scale.linear().domain([0,5])) .group({all: function(){ return [ {key : 0, value : 4}, {key : 2, value : 14}, {key : 3, value : 20}, ]; }}); 400级错误
  • GuzzleHttp\Exception\ClientException表示500级错误
  • GuzzleHttp\Exception\ServerException两者(这是他们的超类)

处理此类错误的代码现在看起来像这样:

GuzzleHttp\Exception\BadResponseException

答案 1 :(得分:60)

Guzzle 3.x

Per the docs,您可以捕获相应的异常类型({4}错误ClientErrorResponseException)并调用其getResponse()方法获取响应对象,然后调用getBody() on的是:

use Guzzle\Http\Exception\ClientErrorResponseException;

...

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

true传递给getBody函数表示您希望将响应正文作为字符串获取。否则,您将把它作为类Guzzle\Http\EntityBody的实例。

答案 2 :(得分:40)

虽然上面的答案是好的,但它们不会处理网络错误,因为Mark提到BadResponseException只是ClientException和ServerException的超类。但RequestException也是BadRequestException的超类。这不仅会捕获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);
  }

}

答案 3 :(得分:7)

如果将'http_errors' => false放在耗时的请求选项中,则它将在出现4xx或5xx错误时停止引发异常,例如:$client->get(url, ['http_errors' => false])。然后解析响应,不管是好的还是错误的,都会在响应中 for more info

答案 4 :(得分:3)

问题是:

<块引用>

当服务器返回 4xx 和 5xx 状态代码时,我想处理 Guzzle 的错误

虽然您可以专门处理 4xx 或 5xx 状态代码,但在实践中捕获所有异常并相应地处理结果是有意义的。

问题也是,你是只想处理错误还是获取正文?我认为在大多数情况下,处理错误而不获取消息正文或仅在非错误的情况下获取正文就足够了。

我会查看文档以检查您的 Guzzle 版本如何处理它,因为这可能会发生变化:https://docs.guzzlephp.org/en/stable/quickstart.html#exceptions

另请参阅 "Working with errors" 上的官方文档中的此页面,其中指出:

<块引用>

收到 4xx 或 5xx 响应的请求将抛出 Guzzle\Http\Exception\BadResponseException。更具体地说,4xx 错误会引发 Guzzle\Http\Exception\ClientErrorResponseException5xx 错误会引发 Guzzle\Http\Exception\ServerErrorResponseException。您可以捕获特定异常或仅捕获 BadResponseException 以处理任一类型的错误。

Guzzle 7(来自文档):

. \RuntimeException
└── TransferException (implements GuzzleException)
    └── RequestException
        ├── BadResponseException
        │   ├── ServerException
        │   └── ClientException
        ├── ConnectException
        └── TooManyRedirectsException

因此,您的代码可能如下所示:

try {
    $response = $client->request('GET', $url);
    if ($response->getStatusCode() >= 300) {
       // is HTTP status code (for non-exceptions) 
       $statusCode = $response->getStatusCode();
       // handle error 
    } else {
      // is valid URL
    }
            
} catch (TooManyRedirectsException $e) {
    // handle too many redirects
} catch (ClientException | ServerException $e) {
    // ClientException - A GuzzleHttp\Exception\ClientException is thrown for 400 level errors if the http_errors request option is set to true.
    // ServerException - A GuzzleHttp\Exception\ServerException is thrown for 500 level errors if the http_errors request option is set to true.
    if ($e->hasResponse()) {
       // is HTTP status code, e.g. 500 
       $statusCode = $e->getResponse()->getStatusCode();
    }
} catch (ConnectException $e) {
    // ConnectException - A GuzzleHttp\Exception\ConnectException exception is thrown in the event of a networking error. This may be any libcurl error, including certificate problems
    $handlerContext = $e->getHandlerContext();
    if ($handlerContext['errno'] ?? 0) {
       // this is the libcurl error code, not the HTTP status code!!!
       // for example 6 for "Couldn't resolve host"
       $errno = (int)($handlerContext['errno']);
    } 
    // get a description of the error (which will include a link to libcurl page)
    $errorMessage = $handlerContext['error'] ?? $e->getMessage();
         
} catch (\Exception $e) {
    // fallback, in case of other exception
}

如果你真的需要尸体,你可以像往常一样取回它:

https://docs.guzzlephp.org/en/stable/quickstart.html#using-responses

$body = $response->getBody();

答案 5 :(得分:2)

从2019年开始,这是我根据上面的回答和Guzzle docs来详细说明的内容,以处理异常,获取响应正文,状态代码,消息以及其他有时有价值的响应项。

try {
    /**
     * We use Guzzle to make an HTTP request somewhere in the
     * following theMethodMayThorwException().
     */
    $result = theMethodMayThorwException();
} catch (\Exception $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.
     */
    $response = $e->getResponse();
    var_dump($response->getStatusCode()); // HTTP status code
    var_dump($response->getReasonPhrase()); // Message
    var_dump((string) $response->getBody()); // Body
    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
}

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

注释:

使用catch子句,我们捕获了继承链PHP根异常类 \Exception随着Guzzle自定义异常的扩展。

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

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

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

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

如果您直接致电Guzzle,则可以根据自己的用例情况来了解GuzzleHttp\Exception\RequestExceptiontheir exceptions docs中提到的其他任何人。

答案 6 :(得分:2)

以上任何响应均不适用于没有正文但仍包含一些描述文字的错误。对我来说,这是SSL certificate problem: unable to get local issuer certificate错误。所以我直接看了一下代码,因为doc并没有说太多,而是这样做的(在 Guzzle 7.1 中):

try {
    // call here
} catch (\GuzzleHttp\Exception\RequestException $e) {
    if ($e->hasResponse()) {
        $response = $e->getResponse();
        // message is in $response->getReasonPhrase()
    } else {
        $response = $e->getHandlerContext();
        if (isset($response['error'])) {
            // message is in $response['error']
        } else {
            // Unknown error occured!
        }
    }
}

答案 7 :(得分:0)

您可以获得整个错误消息(未截断)。 请尝试以下代码:

try {
    ...
} catch (GuzzleHttp\Exception\RequestException $e) {
    $error = \GuzzleHttp\Psr7\str($e->getResponse());
    print_r($error);
}

答案 8 :(得分:0)

对我来说,这与 Laravel 包中的 Guzzle 一起工作:

try {
    $response = $this->client->get($url);
}
catch(\Exception $e) {
    $error = $e->getResponse();
    dd($error);
}