Обробіть виключення Guzzle та отримайте корпус HTTP


122

Я хотів би обробляти помилки від Guzzle, коли сервер повертає 4хx та 5xx коди стану. Я роблю такий запит:

$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-відповіді. Як я можу отримати орган відповіді?


1
Це питання пов'язане з цим питанням stackoverflow.com/questions/17658283/…, і відповіді, можливо, теж допоможуть.
Trendfischer

Відповіді:


84

Гузька 3.x

За документами ви можете зловити відповідний тип винятку ( ClientErrorResponseExceptionдля помилок 4xx) та зателефонувати на його getResponse()метод, щоб отримати об’єкт відповіді, а потім зателефонуйте getBody()на це:

use Guzzle\Http\Exception\ClientErrorResponseException;

...

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

Перехід trueдо getBodyфункції означає, що ви хочете отримати тіло відповіді у вигляді рядка. Інакше ви отримаєте його як екземпляр класу Guzzle\Http\EntityBody.


232

Плитка 6.х

За документами типи винятків, які вам можуть знадобитися, є:

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

12
Для мене $response->getBody()->getContents()повернувся б порожній рядок. Потім я натрапив на це в документах : \GuzzleHttp\Psr7\str($e->getResponse()) Передача відповіді як Psr7 String отримала мені добре відформатоване та повне повідомлення про помилку.
Енді Місце

3
@AndyPlace після того, як поглянув на PSR 7 (на який не було посилання в розділі документів, на які я посилаюся в той час, коли я писав цю відповідь, але зараз), мені не відразу зрозуміло, чому дзвінки Psr7\str()матимуть різні результати до ->getContents(). Чи є у вас мінімальний приклад, що демонструє це, що може дати мені зрозуміти це і, можливо, оновити цю відповідь?
Марк Амері

24
Варто зазначити, що 'http_errors' => falseваріант може бути переданий у запиті Guzzle, який забороняє кидати винятки. Потім ви можете отримати тіло $response->getBody()незалежно від того, який код статусу, і ви можете протестувати код статусу, якщо це необхідно $response->getStatusCode().
тремтіння

2
Оскільки @AndyPlace $response->getBody()->getContents()дає мені порожній рядок в одному випадку, я не розумію, чому. Але використовуючи \GuzzleHttp\Psr7\str()повертає всю відповідь HTTP як рядок, і я би тільки HTTP-тіло. Як сказано в документації , тіло можна використовувати, кидаючи його на рядок. $stringBody = (string) $clientException->getResponse()->getBody();
AnthonyB

1
Це зробило це для мене, хоча я \GuzzleHttp\Exception\RequestExceptionнатомість отримував 400код статусу. спробуйте {$ request-> api ('POST', 'endpoint.json'); } catch (RequestException $ e) {print_r ($ e-> getResponse () -> getBody () -> getContents ()); }
jpcaparas

54

Хоча відповіді вище хороші, вони не вловлюють мережеві помилки. Як згадував Марк, 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);
  }

}

це JsonResponseклас від Guzzle?
aexl

JsonResponseприходить від Symfony
глава

14

Станом на 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пункту ми \Exceptionвловлюємо клас виключення кореневого класу PHP root наслідок, оскільки користувацькі винятки Guzzle розширюють його.

Цей підхід може бути корисним для випадків, коли Guzzle використовується під кришкою, як у Laravel або AWS API PHP SDK, тому ви не можете зловити справжнє виключення Guzzle.

У цьому випадку класом виключень може бути не той, який згадується в документах Guzzle (наприклад, GuzzleHttp\Exception\RequestExceptionяк кореневий виняток для Guzzle).

Тож вам доведеться ловити \Exceptionзамість цього, але майте на увазі, що це все-таки екземпляр класу виключень Guzzle.

Хоча використовуйте обережно. Ці обгортки можуть зробити $e->getResponse()справжні методи об'єкта Guzzle недоступними. У цьому випадку вам доведеться переглянути фактичний вихідний код обгортки та дізнатися, як отримати статус, повідомлення тощо, а не використовувати методи Guzzle $response.

Якщо ви телефонуєте безпосередньо в Guzzle, ви можете спіймати GuzzleHttp\Exception\RequestExceptionбудь-який інший, зазначений у документах про їх винятки, щодо умов вашого використання.


1
Ви не повинні викликати методи на своєму $responseоб'єкті під час обробки винятків, якщо ви не встановили прапорець $e->hasResponse(), інакше це $responseможе бути nullі будь-який виклик методу призведе до фатальної помилки.
pwaring

@pwaring, правда. Точно так, як говорять документи винятків Guzzle. Оновлено відповідь. Дякую.
Валентина Ши

1
... але це все ще проблематично після виправлення. Ви ловите всі винятки, не тільки Guzzle, але потім закликаєте $e->hasResponseрезультат, метод, який, звичайно, не існує для виключень, що не стосуються Guzzle. Отже, якщо ви підняли виняток theMethodMayThrowException(), що не стосується Guzzle , цей код зафіксує його, спробуйте викликати неіснуючий метод і завершити роботу через неіснуючий метод, фактично приховуючи справжню причину помилки. Переважно було б ловити, GuzzleHttp\Exception\RequestExceptionа Exceptionне уникати цього.
Марк Амерді

1
@MarkAmery, ваша думка цілком справедлива. Дякую. Я оновив орган відповідей.
Валентина Ши

1
@JaberAlNahian рада почути :) це був мій намір. Завжди ласкаво просимо.
Валентина Ши

4

якщо покласти 'http_errors' => falseв жерти параметри запиту, то це було б зупинити кинути виняток при прибуде 4xx або 5xx помилки, як це: $client->get(url, ['http_errors' => false]). то ви аналізуєте відповідь, неважливо, це добре чи помилка, це буде у відповіді для отримання додаткової інформації


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