151

当服务器返回 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 响应的正文。如何获取响应正文?

4

10 回答 10

286

狂饮 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();
}
于 2015-06-20T18:20:58.490 回答
99

狂饮 3.x

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

use Guzzle\Http\Exception\ClientErrorResponseException;

...

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

传递truegetBody函数表示您希望将响应正文作为字符串获取。否则你会得到它作为 class 的实例Guzzle\Http\EntityBody

于 2013-11-02T23:52:04.587 回答
64

虽然上面的答案很好,但它们不会捕获网络错误。正如 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);
  }

}
于 2016-11-17T00:06:01.190 回答
29

截至 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. ...

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

旁注:

有了catch子句,我们捕获了继承链 PHP 根异常类 \Exception,因为 Guzzle 自定义异常对其进行了扩展。

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

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

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

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

如果您自己直接调用 Guzzle,您可以捕获或在他们的异常文档GuzzleHttp\Exception\RequestException中提到的与您的用例条件相关的任何其他问题。

于 2019-06-30T07:24:51.917 回答
9

如果放入'http_errors' => falseguzzle 请求选项,那么它会在得到 4xx 或 5xx 错误时停止抛出异常,如下所示:$client->get(url, ['http_errors' => false]). 然后你解析响应,无论它是好的还是错误,它都会在响应中 以获取更多信息

于 2020-02-08T10:49:29.433 回答
6

问题是:

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

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

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

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

另请参阅有关“处理错误”的官方文档中的此页面,其中指出:

收到 4xx 或 5xx 响应的请求将抛出Guzzle\Http\Exception\BadResponseException. 更具体地说,4xx错误会抛出 a Guzzle\Http\Exception\ClientErrorResponseException,而5xx错误会抛出 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();

于 2021-01-12T15:20:12.760 回答
5

以上响应均不适用于没有正文但仍有一些描述性文本的错误。对我来说,这是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!
        }
    }
}
于 2020-09-29T08:47:02.833 回答
4

异常应该是 BadResponseException 的一个实例,它有一个 getResponse 方法。然后,您可以将响应正文转换为字符串。参考:https ://github.com/guzzle/guzzle/issues/1105

use GuzzleHttp\Exception\BadResponseException;

$url = $this->baseUrl . "subnet?section=$section";
try {
    $response = $this->client->get($url);
    $subnets = json_decode($response->getBody(), true);
    return $subnets['subnets'];
} catch (BadResponseException $ex) {
    $response = $ex->getResponse();
    $jsonBody = (string) $response->getBody();
    // do something with json string...
}
于 2021-09-09T21:52:42.313 回答
0

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

try {
    ...
} catch (GuzzleHttp\Exception\RequestException $e) {
    $error = \GuzzleHttp\Psr7\str($e->getResponse());
    print_r($error);
}
于 2021-05-31T21:25:59.807 回答
0

对我来说,这与 Laravel 包中的 Guzzle 一起使用:

try {
    $response = $this->client->get($url);
}
catch(\Exception $e) {
    $error = $e->getResponse();
    dd($error);
}
于 2021-07-07T13:26:05.283 回答