Виловлювання винятків із Guzzle


80

Я намагаюся вловити винятки з набору тестів, які я запускаю в 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 дозволило мені правильно вловити 404, але, здається, це не працює для 500 чи 401 відповідей. Хтось може підказати, де я помиляюся, будь ласка.


3
Це код під простором імен? Якщо так, то, крім випадків, коли ви useвикористовуєте винятки, можливо, вам доведеться додати до них префікс ``, щоб явно вказати клас FQ. Так, наприклад, '\ Guzzle \ Http \ Exception \ ClientErrorResponseException'
Ентоні Стерлінг

Відповіді:


17

Якщо виняток кидається в цей tryблок, то в гіршому випадку Exceptionслід ловити щось незахоплене.

Вважайте, що перша частина тесту - це викидання винятку та обгортання цього також у tryблок.


1
Ви маєте рацію, був тест поза межами спроби / лову, який кидав виняток. Дурна помилка, дякую за допомогу.
Ерік

146

Залежно від вашого проекту, може знадобитися відключити винятки для guzzle. Іноді правила кодування забороняють винятки для управління потоком. Ви можете вимкнути винятки для Guzzle 3 таким чином:

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

Це не відключає винятки для curl для чогось на зразок тайм-аутів, але тепер ви можете легко отримати кожен код стану:

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

Щоб перевірити, якщо у вас дійсний код, ви можете використати щось на зразок цього:

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

... або краще обробляти всі очікувані коди:

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...");
}

Для Guzzle 5.3

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

Завдяки @mika

Для Guzzle 6

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

10
У вас коли-небудь була дивна помилка, спричинена відсутністю break;-) Але, звичайно, це було б гарним рішенням, якщо у вас є кілька кодів стану, з якими вам доводиться обробляти однаково. Я вважаю за краще if, тому що перемикач просто підтримує ==.
Trendfischer 02.03.15

Дякую за згадку request.options. Вирішив мою проблему і врятував мене шукати її належним чином. :)
DanielM

2
Або в Guzzle5.3: $ client = new \ GuzzleHttp \ Client (['defaults' => ['exceptions' => false]]);
mika

Це врятувало мій бекон на терміновому проекті. Дякую Trendfischer і ТАК!
Ден Баррон,

46

Щоб виявити помилки Guzzle, ви можете зробити щось подібне:

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

... але, щоб мати можливість «ввійти» або «повторно відправити» ваш запит, спробуйте щось подібне:

// 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();
        }

});

... або якщо ви хочете "зупинити поширення подій", ви можете замінити прослуховувач подій (з вищим пріоритетом, ніж -255) і просто зупинити розповсюдження подій.

$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();
    }
});

це хороша ідея запобігти появі помилок, таких як:

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

у вашій заявці.


6
Це вже неможливо в Guzzle 6. Будь-яка ідея, як це зробити за допомогою проміжного програмного забезпечення?
fnagel

30

У моєму випадку я кидав Exceptionна файл з простором імен, тому php намагався зловити, My\Namespace\Exceptionтому взагалі не ловив жодних винятків.

Варто перевірити, чи catch (Exception $e)знаходить потрібний Exceptionклас.

Просто спробуйте catch (\Exception $e)(з цим \там) і перевірте, чи це працює.


4
Мені б хотілося, щоб я прокрутив до цієї помилки вперше, коли у мене виникло те саме питання. Для мене я використовував застарілі імена Guzzle Exception і не вловлював загальний виняток, тому що я не був у кореневій програмі Namesapce. Додавання зворотної косої риски перед винятком почало ловити загальний виняток, що дозволяє мені бачити помилки невідповідності мого імені в більш конкретних винятках Guzzle. Див. Коментарі на stackoverflow.com/a/7892917/2829359 .
Карсон Еванс

Це було саме те, що у мене теж було. Хороша відповідь
Прасад Раджапакша,


5

Старе питання, але Guzzle додає відповідь у межах об’єкта виключення. Тож просто спробуйте, GuzzleHttp\Exception\ClientExceptionа потім скористайтеся getResponseцим винятком, щоб побачити, яка помилка на 400 рівнях, і продовжити звідти.


2

Я ловив, GuzzleHttp\Exception\BadResponseExceptionяк пропонує @dado. Але одного разу я зрозумів, що GuzzleHttp\Exception\ConnectExceptionDNS для домену був недоступний. Тож моя пропозиція - ловіть, GuzzleHttp\Exception\ConnectExceptionщоб перестрахуватися і щодо помилок DNS.


звучить так, ніби ви повинні ловити, GuzzleHttp\Exception\RequestExceptionхто є батьківським ConnectException, BadResponseExceptionі TooManyRedirectsException.
Полум'я

1

Я хочу оновити відповідь на обробку винятків у Psr-7 Guzzle, Guzzle7 та HTTPClient (виразний, мінімальний API навколо клієнта Guzzle HTTP, наданий laravel).

Guzzle7 (те саме працює і для Guzzle 6)

Використовуючи RequestException , RequestException ловить будь-які винятки, які можуть виникнути під час передачі запитів.

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]);
}

Для 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
 }

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.