Виловлювання винятків із 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.