Gestisci l'eccezione Guzzle e ottieni il corpo HTTP


122

Vorrei gestire gli errori di Guzzle quando il server restituisce i codici di stato 4xx e 5xx. Faccio una richiesta come questa:

$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->getMessagerestituisce le informazioni sul codice ma non il corpo della risposta HTTP. Come posso ottenere il corpo della risposta?


1
Questa domanda è correlata a questa domanda stackoverflow.com/questions/17658283/… e anche le risposte potrebbero essere di qualche aiuto.
Trendfischer

Risposte:


84

Guzzle 3.x

Secondo i documenti , puoi catturare il tipo di eccezione appropriato ( ClientErrorResponseExceptionper errori 4xx) e chiamare il suo getResponse()metodo per ottenere l'oggetto risposta, quindi chiamarlo getBody():

use Guzzle\Http\Exception\ClientErrorResponseException;

...

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

Il passaggio truealla getBodyfunzione indica che si desidera ottenere il corpo della risposta come una stringa. Altrimenti lo otterrai come istanza di classe Guzzle\Http\EntityBody.


232

Guzzle 6.x

Secondo i documenti , i tipi di eccezione che potresti dover rilevare sono:

  • GuzzleHttp\Exception\ClientException per errori di livello 400
  • GuzzleHttp\Exception\ServerException per errori di livello 500
  • GuzzleHttp\Exception\BadResponseException per entrambi (è la loro superclasse)

Il codice per gestire tali errori quindi ora assomiglia a questo:

$client = new GuzzleHttp\Client;
try {
    $client->get('http://google.com/nosuchpage');    
}
catch (GuzzleHttp\Exception\ClientException $e) {
    $response = $e->getResponse();
    $responseBodyAsString = $response->getBody()->getContents();
}

12
Per me $response->getBody()->getContents()restituirebbe una stringa vuota. Poi mi sono imbattuto in questo nei documenti : \GuzzleHttp\Psr7\str($e->getResponse()) Casting la risposta come una stringa Psr7 mi ha fatto ottenere un messaggio di errore ben formattato e completo.
Andy Place

3
@AndyPlace dopo aver dato un'occhiata a PSR 7 (che non era referenziato dalla sezione dei documenti a cui mi collego al momento in cui ho scritto questa risposta, ma lo è ora) non è immediatamente ovvio per me perché la chiamata Psr7\str()avrebbe risultati diversi a ->getContents(). Hai un esempio minimo che lo dimostri, che potrebbe farmi capire questo e forse aggiornare questa risposta?
Mark Amery

24
Vale la pena ricordare che l' 'http_errors' => falseopzione può essere passata nella richiesta Guzzle che disabilita il lancio di eccezioni. È quindi possibile ottenere il corpo $response->getBody()indipendentemente dal codice di stato e, se necessario, testarlo con $response->getStatusCode().
tremby

2
Come @AndyPlace, $response->getBody()->getContents()mi dà una stringa vuota in un caso, non capisco perché. Ma l'utilizzo \GuzzleHttp\Psr7\str()restituisce tutta la risposta HTTP come stringa e vorrei solo il corpo HTTP. Come detto nella documentazione , il corpo può essere utilizzato gettandolo a corda. $stringBody = (string) $clientException->getResponse()->getBody();
AnthonyB

1
Questo ha funzionato per me, anche se stavo ottenendo un \GuzzleHttp\Exception\RequestExceptioninvece che restituiva un 400codice di stato. prova {$ request-> api ('POST', 'endpoint.json'); } catch (RequestException $ e) {print_r ($ e-> getResponse () -> getBody () -> getContents ()); }
jpcaparas

54

Sebbene le risposte precedenti siano buone, non rileveranno errori di rete. Come accennato da Mark, BadResponseException è solo una super classe per ClientException e ServerException. Ma RequestException è anche una super classe di BadResponseException. RequestException verrà lanciata non solo per errori 400 e 500, ma anche per errori di rete e reindirizzamenti infiniti. Quindi supponiamo che tu richieda la pagina seguente ma la tua rete sta funzionando e il tuo trucco si aspetta solo una BadResponseException. Bene, la tua applicazione genererà un errore.

In questo caso è meglio aspettarsi RequestException e controllare una risposta.

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

}

è JsonResponseuna classe di Guzzle?
aexl

JsonResponseproviene da Symfony
capitolo

14

A partire dal 2019 ecco cosa ho elaborato dalle risposte sopra e dai documenti di Guzzle per gestire l'eccezione, ottenere il corpo della risposta, il codice di stato, il messaggio e gli altri elementi di risposta a volte preziosi.

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

Ecco. Ottieni le informazioni sulla risposta in elementi opportunamente separati.

Note a margine:

Con la catchclausola \Exceptioncatturiamo la classe di eccezione radice PHP della catena di ereditarietà poiché le eccezioni personalizzate di Guzzle la estendono.

Questo approccio può essere utile per i casi d'uso in cui Guzzle viene utilizzato sotto il cofano come in Laravel o AWS API PHP SDK in modo da non poter catturare la vera eccezione Guzzle.

In questo caso, la classe dell'eccezione potrebbe non essere quella menzionata nella documentazione di Guzzle (ad esempio GuzzleHttp\Exception\RequestExceptioncome eccezione principale per Guzzle).

Quindi devi prendere \Exceptioninvece, ma tieni presente che è ancora l'istanza della classe di eccezione Guzzle.

Anche se usa con cura. Questi wrapper potrebbero rendere $e->getResponse()i metodi originali dell'oggetto Guzzle non disponibili. In questo caso, dovrai guardare il codice sorgente dell'eccezione effettiva del wrapper e scoprire come ottenere lo stato, il messaggio, ecc. Invece di usare $responsei metodi di Guzzle .

Se chiami Guzzle direttamente tu stesso puoi catturare GuzzleHttp\Exception\RequestExceptiono qualsiasi altro menzionato nei loro documenti di eccezioni rispetto alle condizioni del tuo caso d'uso.


1
Non dovresti chiamare metodi sul tuo $responseoggetto durante la gestione delle eccezioni a meno che tu non abbia controllato $e->hasResponse(), altrimenti $responsepotrebbe essere nulle qualsiasi chiamata di metodo causerà un errore fatale.
pwaring

@pwaring, true. Esattamente come dicono i documenti sulle eccezioni di Guzzle. Aggiornata la risposta. Grazie.
Valentine Shi

1
... ma questo è ancora problematico dopo la correzione. Stai catturando tutte le eccezioni, non solo quelle Guzzle, ma poi stai invocando $e->hasResponseil risultato, un metodo che, ovviamente, non esiste per le eccezioni non Guzzle. Quindi, se sollevi un'eccezione non Guzzle da theMethodMayThrowException(), questo codice la catturerà, proverà a chiamare un metodo inesistente e si bloccherà a causa del metodo inesistente, nascondendo effettivamente la vera causa dell'errore. Sarebbe preferibile catturare GuzzleHttp\Exception\RequestExceptioninvece di Exceptionevitarlo.
Mark Amery

1
@MarkAmery, il tuo punto è perfettamente valido. Grazie. Ho aggiornato il corpo della risposta.
Valentine Shi

1
@JaberAlNahian felice di sentire :) quella era la mia intenzione. Sempre il benvenuto.
Valentine Shi

4

Se messo 'http_errors' => falsein opzioni richiesta gozzovigliare, allora si fermava eccezione tiro mentre get 4xx o 5xx errore, in questo modo: $client->get(url, ['http_errors' => false]). quindi analizzi la risposta, non importa se è ok o errore, sarebbe nella risposta per maggiori informazioni


Questa domanda riguarda gli errori di gestione che non
richiedono l'interruzione delle
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.