Behandeln Sie die Guzzle-Ausnahme und holen Sie sich den HTTP-Body

122

Ich möchte Fehler von Guzzle behandeln, wenn der Server 4xx- und 5xx-Statuscodes zurückgibt. Ich mache eine Anfrage wie folgt:

$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->getMessageGibt Code-Informationen zurück, jedoch nicht den Text der HTTP-Antwort. Wie kann ich den Antworttext erhalten?

Domos
quelle
1
Diese Frage bezieht sich auf diese Frage stackoverflow.com/questions/17658283/… und die Antworten dort könnten ebenfalls hilfreich sein.
Trendfischer

Antworten:

84

Guzzle 3.x.

Gemäß den Dokumenten können Sie den entsprechenden Ausnahmetyp ( ClientErrorResponseExceptionfür 4xx-Fehler) abfangen und seine getResponse()Methode aufrufen , um das Antwortobjekt abzurufen, und dann Folgendes aufrufen getBody():

use Guzzle\Http\Exception\ClientErrorResponseException;

...

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

Wenn Sie truean die getBodyFunktion übergeben, wird angezeigt, dass Sie den Antworttext als Zeichenfolge erhalten möchten. Andernfalls erhalten Sie es als Instanz der Klasse Guzzle\Http\EntityBody.

Sebbo
quelle
232

Guzzle 6.x.

Gemäß den Dokumenten müssen Sie möglicherweise folgende Ausnahmetypen abfangen:

  • GuzzleHttp\Exception\ClientException für 400-Level-Fehler
  • GuzzleHttp\Exception\ServerException für 500-Level-Fehler
  • GuzzleHttp\Exception\BadResponseException für beide (es ist ihre Superklasse)

Der Code zur Behandlung solcher Fehler sieht nun ungefähr so ​​aus:

$client = new GuzzleHttp\Client;
try {
    $client->get('http://google.com/nosuchpage');    
}
catch (GuzzleHttp\Exception\ClientException $e) {
    $response = $e->getResponse();
    $responseBodyAsString = $response->getBody()->getContents();
}
Mark Amery
quelle
12
Für mich $response->getBody()->getContents()würde eine leere Zeichenfolge zurückgeben. Ich bin dann in den Dokumenten darauf gestoßen : \GuzzleHttp\Psr7\str($e->getResponse()) Wenn ich die Antwort als Psr7-String umwandelte, erhielt ich eine gut formatierte und vollständige Fehlermeldung.
Andy Place
3
@AndyPlace Nachdem ich einen Blick auf PSR 7 geworfen habe (auf den in dem Abschnitt der Dokumente, auf den ich zum Zeitpunkt des Schreibens dieser Antwort verweise, nicht verwiesen wurde, aber jetzt), ist mir nicht sofort klar, warum das Anrufen Psr7\str()unterschiedliche Ergebnisse haben würde zu ->getContents(). Haben Sie ein minimales Beispiel, das dies demonstriert, damit ich das verstehen und vielleicht diese Antwort aktualisieren kann?
Mark Amery
24
Erwähnenswert ist, dass die 'http_errors' => falseOption in der Guzzle-Anforderung übergeben werden kann, wodurch das Auslösen von Ausnahmen deaktiviert wird. Sie können dann den Body $response->getBody()unabhängig vom Statuscode abrufen und den Statuscode bei Bedarf mit testen $response->getStatusCode().
Tremby
2
Da @AndyPlace $response->getBody()->getContents()mir in einem Fall eine leere Zeichenfolge gibt, verstehe ich nicht warum. Aber using \GuzzleHttp\Psr7\str()gibt die gesamte HTTP-Antwort als Zeichenfolge zurück, und ich würde nur den HTTP-Body verwenden. Wie in der Dokumentation erwähnt , kann der Körper verwendet werden, indem er in einen String umgewandelt wird. $stringBody = (string) $clientException->getResponse()->getBody();
AnthonyB
1
Dies hat es für mich getan, obwohl ich \GuzzleHttp\Exception\RequestExceptionstattdessen einen bekam, der einen 400Statuscode zurückgab . try {$ request-> api ('POST', 'endpoint.json'); } catch (RequestException $ e) {print_r ($ e-> getResponse () -> getBody () -> getContents ()); }
jpcaparas
54

Obwohl die obigen Antworten gut sind, werden sie keine Netzwerkfehler abfangen. Wie Mark erwähnt hat, ist BadResponseException nur eine Superklasse für ClientException und ServerException. RequestException ist aber auch eine Superklasse von BadResponseException. RequestException wird nicht nur für 400 und 500 Fehler ausgelöst, sondern auch für Netzwerkfehler und unendliche Weiterleitungen. Angenommen, Sie fordern die folgende Seite an, aber Ihr Netzwerk spielt gerade und Ihr Fang erwartet nur eine BadResponseException. Nun, Ihre Anwendung wird einen Fehler auslösen.

In diesem Fall ist es besser, RequestException zu erwarten und nach einer Antwort zu suchen.

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

}
Kerl
quelle
ist JsonResponseeine Klasse von Guzzle?
Aexl
JsonResponsekommt von Symfony
Kap
14

Ab 2019 habe ich aus den obigen Antworten und den Guzzle-Dokumenten Folgendes herausgearbeitet , um die Ausnahme zu behandeln, den Antworttext, den Statuscode, die Nachricht und die anderen manchmal wertvollen Antwortelemente abzurufen.

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

Voila. Sie erhalten die Informationen der Antwort in bequem getrennten Elementen.

Randnotizen:

Mit catchKlausel fangen wir die PHP-Root-Ausnahmeklasse der Vererbungskette \Exceptionab, wenn benutzerdefinierte Guzzle-Ausnahmen sie erweitern.

Dieser Ansatz kann für Anwendungsfälle nützlich sein, in denen Guzzle wie in Laravel oder AWS API PHP SDK unter der Haube verwendet wird, sodass Sie die echte Guzzle-Ausnahme nicht abfangen können.

In diesem Fall ist die Ausnahmeklasse möglicherweise nicht die in den Guzzle-Dokumenten erwähnte (z GuzzleHttp\Exception\RequestException als Root-Ausnahme für Guzzle).

Sie müssen also \Exceptionstattdessen fangen, aber denken Sie daran, dass es sich immer noch um die Guzzle-Ausnahmeklasseninstanz handelt.

Obwohl mit Vorsicht verwenden. Diese Wrapper machen möglicherweise $e->getResponse()die echten Methoden des Guzzle- Objekts nicht verfügbar. In diesem Fall müssen Sie sich den tatsächlichen Ausnahmequellcode des Wrappers ansehen und herausfinden, wie Sie Status, Nachricht usw. abrufen können $response, anstatt die Methoden von Guzzle zu verwenden.

Wenn Sie Guzzle direkt selbst anrufen, können Sie GuzzleHttp\Exception\RequestExceptionoder andere in den Ausnahmedokumenten erwähnte Dokumente in Bezug auf Ihre Anwendungsfallbedingungen abfangen .

Valentine Shi
quelle
1
Sie sollten $responsebei der Behandlung von Ausnahmen keine Methoden für Ihr Objekt aufrufen, es sei denn, Sie haben dies überprüft $e->hasResponse(). Andernfalls $responsekann es vorkommen, nulldass Methodenaufrufe einen schwerwiegenden Fehler verursachen.
Pwaring
@pwaring, stimmt. Genau wie in den Guzzle-Ausnahmen angegeben. Die Antwort wurde aktualisiert. Danke dir.
Valentine Shi
1
... aber das ist nach dem Fix immer noch problematisch. Sie fangen alle Ausnahmen ab, nicht nur die Guzzle-Ausnahmen, sondern rufen dann $e->hasResponsedas Ergebnis auf, eine Methode, die es natürlich nicht für Nicht-Guzzle-Ausnahmen gibt. Wenn Sie also eine Nicht-Guzzle-Ausnahme von auslösen theMethodMayThrowException(), wird dieser Code diese abfangen, versuchen, eine nicht vorhandene Methode aufzurufen, und aufgrund der nicht vorhandenen Methode abstürzen, wodurch die wahre Fehlerursache effektiv ausgeblendet wird. Vorzuziehen wäre zu fangen, GuzzleHttp\Exception\RequestExceptionanstatt dies Exceptionzu vermeiden.
Mark Amery
1
@ MarkAmery, Ihr Punkt ist vollkommen gültig. Danke dir. Ich habe den Antworttext aktualisiert.
Valentine Shi
1
@JaberAlNahian freut sich zu hören :) das war meine Absicht. Immer willkommen.
Valentine Shi
4

Wenn 'http_errors' => falseOptionen für die Verzehranforderung eingegeben werden, wird die Ausnahme ausgelöst, während ein 4xx- oder 5xx-Fehler wie folgt angezeigt wird : $client->get(url, ['http_errors' => false]). Dann analysieren Sie die Antwort, unabhängig davon, ob sie in Ordnung oder fehlerhaft ist. Weitere Informationen finden Sie in der Antwort

user8487819
quelle
Bei dieser Frage geht es um Handle-Fehler, bei denen nicht darum gebeten wird, Fehlerausnahmen zu stoppen
Dlk