Xử lý ngoại lệ Guzzle và lấy HTTP body


122

Tôi muốn xử lý lỗi từ Guzzle khi máy chủ trả về mã trạng thái 4xx và 5xx. Tôi đưa ra một yêu cầu như sau:

$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->getMessagetrả về thông tin mã nhưng không trả về phần nội dung của phản hồi HTTP. Làm thế nào tôi có thể nhận được cơ quan phản hồi?


1
Câu hỏi này có liên quan đến câu hỏi này stackoverflow.com/questions/17658283/… và các câu trả lời cũng có thể có một số trợ giúp.
Trendfischer

Câu trả lời:


84

Câu đố 3.x

Theo tài liệu , bạn có thể bắt loại ngoại lệ thích hợp ( ClientErrorResponseExceptionđối với lỗi 4xx) và gọi getResponse()phương thức của nó để nhận đối tượng phản hồi, sau đó gọi getBody():

use Guzzle\Http\Exception\ClientErrorResponseException;

...

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

Việc truechuyển đến getBodyhàm cho biết rằng bạn muốn nhận phần thân phản hồi dưới dạng một chuỗi. Nếu không, bạn sẽ coi nó như là một thể hiện của lớp Guzzle\Http\EntityBody.


232

Câu đố 6.x

Theo tài liệu , các loại ngoại lệ bạn có thể cần phải nắm bắt là:

  • GuzzleHttp\Exception\ClientException đối với lỗi cấp 400
  • GuzzleHttp\Exception\ServerException đối với lỗi cấp 500
  • GuzzleHttp\Exception\BadResponseException cho cả hai (đó là lớp cha của chúng)

Mã để xử lý các lỗi như vậy bây giờ trông giống như sau:

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

12
Đối với tôi $response->getBody()->getContents()sẽ trả về một chuỗi trống. Sau đó, tôi tình cờ gặp điều này trong tài liệu : \GuzzleHttp\Psr7\str($e->getResponse()) Truyền phản hồi dưới dạng Chuỗi Psr7 khiến tôi nhận được một thông báo lỗi hoàn chỉnh và được định dạng độc đáo.
Andy Place

3
@AndyPlace sau khi xem qua PSR 7 (không được tham chiếu bởi phần tài liệu mà tôi liên kết đến tại thời điểm tôi viết câu trả lời này, nhưng hiện tại), tôi không rõ tại sao việc gọi Psr7\str()lại có kết quả khác đến ->getContents(). Bạn có một ví dụ nhỏ nhất chứng minh điều này không, có thể cho tôi hiểu điều này và có thể cập nhật câu trả lời này?
Mark Amery

24
Đáng nói là 'http_errors' => falsetùy chọn có thể được thông qua trong yêu cầu Guzzle mà vô hiệu hóa các ngoại lệ ném. Sau đó, bạn có thể lấy phần thân mà $response->getBody()không cần biết mã trạng thái là gì và bạn có thể kiểm tra mã trạng thái nếu cần $response->getStatusCode().
run rẩy

2
Là @AndyPlace, $response->getBody()->getContents()cung cấp cho tôi một chuỗi trống trong một trường hợp, tôi không hiểu tại sao. Nhưng sử dụng \GuzzleHttp\Psr7\str()trả về tất cả phản hồi HTTP dưới dạng một chuỗi và tôi sẽ chỉ phần thân HTTP. Như đã nói trong tài liệu , phần thân có thể được sử dụng bằng cách truyền nó thành chuỗi. $stringBody = (string) $clientException->getResponse()->getBody();
AnthonyB

1
Điều này đã làm điều đó cho tôi, mặc dù tôi đã nhận được một \GuzzleHttp\Exception\RequestExceptionthay thế trả lại 400mã trạng thái. thử {$ request-> api ('POST', 'endpoint.json'); } catch (RequestException $ e) {print_r ($ e-> getResponse () -> getBody () -> getContents ()); }
jpcaparas

54

Mặc dù các câu trả lời trên là tốt nhưng chúng sẽ không bắt lỗi mạng. Như Mark đã đề cập, BadResponseException chỉ là một siêu lớp cho ClientException và ServerException. Nhưng RequestException cũng là một siêu lớp của BadResponseException. RequestException sẽ không chỉ xảy ra lỗi 400 và 500 mà còn cả lỗi mạng và chuyển hướng vô hạn. Vì vậy, giả sử bạn yêu cầu trang bên dưới nhưng mạng của bạn đang hoạt động và lỗi của bạn chỉ mong đợi một BadResponseException. Ứng dụng của bạn sẽ gặp lỗi.

Trong trường hợp này, tốt hơn là mong đợi RequestException và kiểm tra phản hồi.

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

}

JsonResponsemột lớp học từ Guzzle?
aexl

JsonResponseđến từ Symfony
chương

14

Kể từ năm 2019, đây là những gì tôi đã xây dựng từ các câu trả lời ở trên và tài liệu Guzzle để xử lý ngoại lệ, lấy nội dung phản hồi, mã trạng thái, tin nhắn và các mục phản hồi đôi khi có giá trị khác.

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

Thì đấy. Bạn nhận được thông tin của phản hồi trong các mục được phân tách thuận tiện.

Ghi chú phụ:

Với catchmệnh đề, chúng ta bắt lớp ngoại lệ gốc PHP chuỗi kế thừa \Exceptionvì các ngoại lệ tùy chỉnh Guzzle mở rộng nó.

Cách tiếp cận này có thể hữu ích cho các trường hợp sử dụng trong đó Guzzle được sử dụng ẩn như trong SDK PHP của Laravel hoặc AWS API, vì vậy bạn không thể bắt được ngoại lệ Guzzle chính hãng.

Trong trường hợp này, lớp ngoại lệ có thể không phải là lớp được đề cập trong tài liệu Guzzle (ví dụ GuzzleHttp\Exception\RequestExceptionnhư ngoại lệ gốc cho Guzzle).

Vì vậy, bạn phải bắt \Exceptionthay thế nhưng hãy nhớ rằng nó vẫn là thể hiện của lớp ngoại lệ Guzzle.

Mặc dù sử dụng cẩn thận. Những trình bao bọc đó có thể làm cho $e->getResponse()các phương thức chính hãng của đối tượng Guzzle không khả dụng. Trong trường hợp này, bạn sẽ phải xem mã nguồn ngoại lệ thực tế của trình bao bọc và tìm cách lấy trạng thái, thông báo, v.v. thay vì sử dụng $responsecác phương pháp của Guzzle .

Nếu bạn tự mình gọi trực tiếp cho Guzzle, bạn có thể bắt gặp GuzzleHttp\Exception\RequestExceptionhoặc bất kỳ ai khác được đề cập trong tài liệu ngoại lệ của họ liên quan đến điều kiện trường hợp sử dụng của bạn.


1
Bạn không nên gọi các phương thức trên $responseđối tượng của mình khi xử lý các ngoại lệ trừ khi bạn đã kiểm tra $e->hasResponse(), nếu không thì $responsecó thể được nullvà bất kỳ lệnh gọi phương thức nào sẽ gây ra lỗi nghiêm trọng.
pwaring

@pwaring, đúng. Chính xác như các tài liệu về ngoại lệ của Guzzle nói. Đã cập nhật câu trả lời. Cảm ơn bạn.
Valentine Shi

1
... nhưng điều này vẫn có vấn đề sau khi sửa chữa. Bạn đang bắt tất cả các ngoại lệ, không chỉ các ngoại lệ, nhưng sau đó bạn đang gọi $e->hasResponsekết quả, một phương pháp, tất nhiên, không tồn tại đối với các ngoại lệ không phải của Giải đố. Vì vậy, nếu bạn đưa ra một ngoại lệ không phải của Guzzle từ theMethodMayThrowException(), mã này sẽ bắt nó, cố gắng gọi một phương thức không tồn tại và gặp sự cố do phương thức không tồn tại, ẩn một cách hiệu quả nguyên nhân thực sự của lỗi. Tốt hơn là bắt GuzzleHttp\Exception\RequestExceptionthay vì Exceptiontránh điều này.
Mark Amery

1
@MarkAmery, quan điểm của bạn hoàn toàn hợp lệ. Cảm ơn bạn. Tôi đã cập nhật nội dung câu trả lời.
Valentine Shi

1
@JaberAlNahian rất vui khi biết :) đó là ý định của tôi. Luôn được chào đón.
Valentine Shi

4

nếu đặt 'http_errors' => falsetrong các tùy chọn theo yêu cầu hay uống rượu, sau đó nó sẽ dừng lại ngoại lệ ném trong khi get 4xx hay 5xx lỗi, như thế này: $client->get(url, ['http_errors' => false]). sau đó bạn phân tích cú pháp phản hồi, không quan trọng nó ổn hay lỗi, nó sẽ nằm trong phản hồi để biết thêm thông tin


Câu hỏi này là về xử lý lỗi không yêu cầu ngoại lệ lỗi dừng
Dlk
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.