从暴饮暴食中捕捉异常

我试图从一组测试中捕捉异常,我正在运行一个 API,我正在开发和使用 Guzzle 消费的 API 方法。我将测试包装在 try/catch 块中,但它仍然抛出未处理的异常错误。按照他们的文档中的描述添加事件侦听器似乎没有任何作用。我需要能够检索到 HTTP 代码为500,401,400的响应,事实上任何不是200的响应,因为系统会根据调用的结果设置最合适的代码,如果它不工作的话。

当前代码示例

foreach($tests as $test){


$client = new Client($api_url);
$client->getEventDispatcher()->addListener('request.error', function(Event $event) {


if ($event['response']->getStatusCode() == 401) {
$newResponse = new Response($event['response']->getStatusCode());
$event['response'] = $newResponse;
$event->stopPropagation();
}
});


try {


$client->setDefaultOption('query', $query_string);
$request = $client->get($api_version . $test['method'], array(), isset($test['query'])?$test['query']:array());




// Do something with Guzzle.
$response = $request->send();
displayTest($request, $response);
}
catch (Guzzle\Http\Exception\ClientErrorResponseException $e) {


$req = $e->getRequest();
$resp =$e->getResponse();
displayTest($req,$resp);
}
catch (Guzzle\Http\Exception\ServerErrorResponseException $e) {


$req = $e->getRequest();
$resp =$e->getResponse();
displayTest($req,$resp);
}
catch (Guzzle\Http\Exception\BadResponseException $e) {


$req = $e->getRequest();
$resp =$e->getResponse();
displayTest($req,$resp);
}
catch( Exception $e){
echo "AGH!";
}


unset($client);
$client=null;


}

即使有了抛出的异常类型的特定 catch 块,我仍然可以返回

Fatal error: Uncaught exception 'Guzzle\Http\Exception\ClientErrorResponseException' with message 'Client error response [status code] 401 [reason phrase] Unauthorized [url]

正如您所预期的那样,页面上的所有执行都将停止。添加 BadResponseException catch 允许我正确捕获404,但是这似乎不适用于500或401响应。有人能告诉我哪里出错了吗。

168963 次浏览

If the Exception is being thrown in that try block then at worst case scenario Exception should be catching anything uncaught.

Consider that the first part of the test is throwing the Exception and wrap that in the try block as well.

To catch Guzzle errors you can do something like this:

try {
$response = $client->get('/not_found.xml')->send();
} catch (Guzzle\Http\Exception\BadResponseException $e) {
echo 'Uh oh! ' . $e->getMessage();
}

... but, to be able to "log" or "resend" your request try something like this:

// Add custom error handling to any request created by this client
$client->getEventDispatcher()->addListener(
'request.error',
function(Event $event) {


//write log here ...


if ($event['response']->getStatusCode() == 401) {


// create new token and resend your request...
$newRequest = $event['request']->clone();
$newRequest->setHeader('X-Auth-Header', MyApplication::getNewAuthToken());
$newResponse = $newRequest->send();


// Set the response object of the request without firing more events
$event['response'] = $newResponse;


// You can also change the response and fire the normal chain of
// events by calling $event['request']->setResponse($newResponse);


// Stop other events from firing when you override 401 responses
$event->stopPropagation();
}


});

... or if you want to "stop event propagation" you can overridde event listener (with a higher priority than -255) and simply stop event propagation.

$client->getEventDispatcher()->addListener('request.error', function(Event $event) {
if ($event['response']->getStatusCode() != 200) {
// Stop other events from firing when you get stytus-code != 200
$event->stopPropagation();
}
});

thats a good idea to prevent guzzle errors like:

request.CRITICAL: Uncaught PHP Exception Guzzle\Http\Exception\ClientErrorResponseException: "Client error response

in your application.

Depending on your project, disabling exceptions for guzzle might be necessary. Sometimes coding rules disallow exceptions for flow control. You can disable exceptions for Guzzle 3 like this:

$client = new \Guzzle\Http\Client($httpBase, array(
'request.options' => array(
'exceptions' => false,
)
));

This does not disable curl exceptions for something like timeouts, but now you can get every status code easily:

$request = $client->get($uri);
$response = $request->send();
$statuscode = $response->getStatusCode();

To check, if you got a valid code, you can use something like this:

if ($statuscode > 300) {
// Do some error handling
}

... or better handle all expected codes:

if (200 === $statuscode) {
// Do something
}
elseif (304 === $statuscode) {
// Nothing to do
}
elseif (404 === $statuscode) {
// Clean up DB or something like this
}
else {
throw new MyException("Invalid response from api...");
}

For Guzzle 5.3

$client = new \GuzzleHttp\Client(['defaults' => [ 'exceptions' => false ]] );

Thanks to @mika

For Guzzle 6

$client = new \GuzzleHttp\Client(['http_errors' => false]);

You need to add a extra parameter with http_errors => false

$request = $client->get($url, ['http_errors' => false]);

I was catching GuzzleHttp\Exception\BadResponseException as @dado is suggesting. But one day I got GuzzleHttp\Exception\ConnectException when DNS for domain wasn't available. So my suggestion is - catch GuzzleHttp\Exception\ConnectException to be safe about DNS errors as well.

Old question, but Guzzle adds the response within the exception object. So a simple try-catch on GuzzleHttp\Exception\ClientException and then using getResponse on that exception to see what 400-level error and continuing from there.

In my case I was throwing Exception on a namespaced file, so php tried to catch My\Namespace\Exception therefore not catching any exceptions at all.

Worth checking if catch (Exception $e) is finding the right Exception class.

Just try catch (\Exception $e) (with that \ there) and see if it works.

I want to update the answer for exception handling in Psr-7 Guzzle, Guzzle7 and HTTPClient(expressive, minimal API around the Guzzle HTTP client provided by laravel).

Guzzle7 (same works for Guzzle 6 as well)

Using RequestException, RequestException catches any exception that can be thrown while transferring requests.

try{
$client = new \GuzzleHttp\Client(['headers' => ['Authorization' => 'Bearer ' . $token]]);
  

$guzzleResponse = $client->get('/foobar');
// or can use
// $guzzleResponse = $client->request('GET', '/foobar')
if ($guzzleResponse->getStatusCode() == 200) {
$response = json_decode($guzzleResponse->getBody(),true);
//perform your action with $response
}
}
catch(\GuzzleHttp\Exception\RequestException $e){
// you can catch here 400 response errors and 500 response errors
// You can either use logs here use Illuminate\Support\Facades\Log;
$error['error'] = $e->getMessage();
$error['request'] = $e->getRequest();
if($e->hasResponse()){
if ($e->getResponse()->getStatusCode() == '400'){
$error['response'] = $e->getResponse();
}
}
Log::error('Error occurred in get request.', ['error' => $error]);
}catch(Exception $e){
//other errors
}

Psr7 Guzzle

use GuzzleHttp\Psr7;
use GuzzleHttp\Exception\RequestException;


try {
$client->request('GET', '/foo');
} catch (RequestException $e) {
$error['error'] = $e->getMessage();
$error['request'] = Psr7\Message::toString($e->getRequest());
if ($e->hasResponse()) {
$error['response'] = Psr7\Message::toString($e->getResponse());
}
Log::error('Error occurred in get request.', ['error' => $error]);
}

For HTTPClient

use Illuminate\Support\Facades\Http;
try{
$response = Http::get('http://api.foo.com');
if($response->successful()){
$reply = $response->json();
}
if($response->failed()){
if($response->clientError()){
//catch all 400 exceptions
Log::debug('client Error occurred in get request.');
$response->throw();
}
if($response->serverError()){
//catch all 500 exceptions
Log::debug('server Error occurred in get request.');
$response->throw();
}
        

}
}catch(Exception $e){
//catch the exception here
}


If you are using the latest version say 6^ and you have a JSON parameter, you can add 'http_errors' => false to the array together with the JSON as seen below enter image description here

I was looking out for away to do this i.e with my JSON in there but couldn't find a straight answer.