Tangani pengecualian Guzzle dan dapatkan isi HTTP

122

Saya ingin menangani kesalahan dari Guzzle ketika server mengembalikan kode status 4xx dan 5xx. Saya membuat permintaan seperti ini:

$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->getMessagemengembalikan info kode tetapi bukan isi respons HTTP. Bagaimana saya bisa mendapatkan respons tubuh?

domos
sumber
1
Pertanyaan ini terkait dengan pertanyaan ini stackoverflow.com/questions/17658283/… dan jawabannya mungkin bisa membantu juga.
Trendfischer

Jawaban:

84

Guzzle 3.x

Sesuai dokumen , Anda dapat menangkap jenis pengecualian yang sesuai ( ClientErrorResponseExceptionuntuk kesalahan 4xx) dan memanggil getResponse()metodenya untuk mendapatkan objek respons, lalu memanggilnya getBody():

use Guzzle\Http\Exception\ClientErrorResponseException;

...

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

Meneruskan trueke getBodyfungsi menunjukkan bahwa Anda ingin mendapatkan isi respons sebagai string. Jika tidak, Anda akan mendapatkannya sebagai contoh kelas Guzzle\Http\EntityBody.

sebbo
sumber
232

Guzzle 6.x

Menurut dokumen , jenis pengecualian yang mungkin perlu Anda tangkap adalah:

  • GuzzleHttp\Exception\ClientException untuk kesalahan 400 level
  • GuzzleHttp\Exception\ServerException untuk kesalahan 500 level
  • GuzzleHttp\Exception\BadResponseException untuk keduanya (itu superclass mereka)

Kode untuk menangani kesalahan seperti itu sekarang terlihat seperti ini:

$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
sumber
12
Bagi saya $response->getBody()->getContents()akan mengembalikan string kosong. Saya kemudian menemukan ini di dokumen : \GuzzleHttp\Psr7\str($e->getResponse()) Mentransmisikan respons sebagai Psr7 String memberi saya pesan kesalahan yang diformat dengan baik dan lengkap.
Andy Place
3
@AndyPlace setelah melihat sekilas ke PSR 7 (yang tidak direferensikan oleh bagian dokumen yang saya tautkan pada saat saya menulis jawaban ini, tetapi sekarang) tidak segera jelas bagi saya mengapa menelepon Psr7\str()akan memiliki hasil yang berbeda kepada ->getContents(). Apakah Anda memiliki contoh minimal yang menunjukkan ini, yang mungkin membuat saya memahami ini dan mungkin memperbarui jawaban ini?
Mark Amery
24
Perlu disebutkan bahwa 'http_errors' => falseopsi dapat diteruskan dalam permintaan Guzzle yang menonaktifkan pengecualian lemparan. Anda kemudian bisa mendapatkan tubuh $response->getBody()tanpa peduli apa kode statusnya, dan Anda bisa menguji kode status jika perlu dengan $response->getStatusCode().
gemetar
2
Karena @AndyPlace, $response->getBody()->getContents()memberi saya string kosong dalam satu kasus, saya tidak mengerti mengapa. Tetapi menggunakan \GuzzleHttp\Psr7\str()mengembalikan semua respons HTTP sebagai string, dan saya hanya akan tubuh HTTP. Seperti yang dikatakan dalam dokumentasi , bodi dapat digunakan dengan mentransmisikannya ke string. $stringBody = (string) $clientException->getResponse()->getBody();
AnthonyB
1
Ini berhasil untuk saya, meskipun saya mendapatkan \GuzzleHttp\Exception\RequestExceptionyang mengembalikan 400kode status. coba {$ request-> api ('POST', 'endpoint.json'); } menangkap (RequestException $ e) {print_r ($ e-> getResponse () -> getBody () -> getContents ()); }
jpcaparas
54

Meskipun jawaban di atas bagus, mereka tidak akan menangkap kesalahan jaringan. Seperti yang disebutkan Mark, BadResponseException hanyalah kelas super untuk ClientException dan ServerException. Tapi RequestException juga merupakan kelas super BadResponseException. RequestException tidak hanya akan terjadi 400 dan 500 kesalahan tetapi juga kesalahan jaringan dan pengalihan tak terbatas. Jadi katakanlah Anda meminta halaman di bawah ini tetapi jaringan Anda sedang bermain dan tangkapan Anda hanya mengharapkan BadResponseException. Nah aplikasi Anda akan membuat kesalahan.

Lebih baik dalam kasus ini mengharapkan RequestException dan memeriksa respons.

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

}
chap
sumber
yang dimaksud JsonResponsedengan kelas dari Guzzle?
aexl
JsonResponseberasal dari Symfony
bab
14

Mulai 2019, inilah yang saya uraikan dari jawaban di atas dan dokumen Guzzle untuk menangani pengecualian, mendapatkan isi respons, kode status, pesan, dan item respons terkadang berharga lainnya.

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. Anda mendapatkan informasi respons dalam item yang dipisahkan dengan mudah.

Catatan Samping:

Dengan catchklausa kita menangkap rantai warisan kelas pengecualian akar PHP \Exceptionsebagai pengecualian khusus Guzzle memperluasnya.

Pendekatan ini mungkin berguna untuk kasus penggunaan di mana Guzzle digunakan secara tersembunyi seperti di Laravel atau AWS API PHP SDK sehingga Anda tidak dapat menangkap pengecualian Guzzle asli.

Dalam kasus ini, kelas pengecualian mungkin bukan yang disebutkan dalam dokumen Guzzle (misalnya GuzzleHttp\Exception\RequestExceptionsebagai pengecualian root untuk Guzzle).

Jadi, Anda harus menangkapnya, \Exceptiontetapi perlu diingat bahwa ini masih merupakan instance kelas pengecualian Guzzle.

Meskipun digunakan dengan hati-hati. Pembungkus tersebut mungkin membuat $e->getResponse()metode asli objek Guzzle tidak tersedia. Dalam kasus ini, Anda harus melihat kode sumber pengecualian aktual pembungkus dan mencari tahu bagaimana mendapatkan status, pesan, dll. Daripada menggunakan $responsemetode Guzzle .

Jika Anda memanggil Guzzle sendiri secara langsung, Anda dapat menangkap GuzzleHttp\Exception\RequestExceptionatau yang lain yang disebutkan dalam dokumen pengecualian mereka sehubungan dengan kondisi kasus penggunaan Anda.

Valentine Shi
sumber
1
Anda tidak boleh memanggil metode pada $responseobjek Anda saat menangani pengecualian kecuali Anda telah memeriksanya $e->hasResponse(), jika tidak $responsemungkin nulldan panggilan metode apa pun akan menyebabkan kesalahan fatal.
pwaring
@pwaring, benar. Persis seperti yang dikatakan dokumen pengecualian Guzzle. Memperbarui jawabannya. Terima kasih.
Valentine Shi
1
... tapi ini masih bermasalah setelah diperbaiki. Anda menangkap semua pengecualian, tidak hanya yang Guzzle, tetapi kemudian Anda memanggil $e->hasResponsehasilnya, sebuah metode yang, tentu saja, tidak ada untuk pengecualian non-Guzzle. Jadi jika Anda memunculkan pengecualian non-Guzzle dari theMethodMayThrowException(), kode ini akan menangkapnya, mencoba memanggil metode yang tidak ada, dan macet karena metode yang tidak ada, secara efektif menyembunyikan penyebab sebenarnya dari kesalahan tersebut. Lebih disukai menangkap GuzzleHttp\Exception\RequestExceptiondaripada Exceptionmenghindari ini.
Mark Amery
1
@MarkAmery, maksud Anda benar-benar valid. Terima kasih. Saya memperbarui isi jawaban.
Valentine Shi
1
@JaberAlNahian senang mendengar :) itu niat saya. Selalu diterima.
Valentine Shi
4

jika put 'http_errors' => falsedalam pilihan permintaan membuang waktu, maka akan berhenti pengecualian melemparkan sementara mendapatkan 4xx atau 5xx error, seperti ini: $client->get(url, ['http_errors' => false]). lalu anda parsing responnya, tidak masalah ok atau error, itu akan di respon untuk info lebih lanjut

pengguna8487819
sumber
Pertanyaan ini adalah tentang menangani kesalahan yang tidak meminta pengecualian kesalahan
penghentian