Guzzle에서 예외 잡기


80

개발중인 API에서 실행중인 일련의 테스트에서 예외를 포착하려고하는데 Guzzle을 사용하여 API 메서드를 사용하고 있습니다. try / catch 블록에 래핑 된 테스트가 있지만 여전히 처리되지 않은 예외 오류가 발생합니다. 문서에 설명 된대로 이벤트 리스너를 추가해도 아무 작업도 수행되지 않는 것 같습니다. HTTP 코드가 500, 401, 400 인 응답을 검색 할 수 있어야합니다. 실제로 작동하지 않는 경우 시스템이 호출 결과에 따라 가장 적절한 코드를 설정하므로 200이 아닌 모든 코드를 검색 할 수 있습니다. .

현재 코드 예

foreach($tests as $test){

        $client = new Client($api_url);
        $client->getEventDispatcher()->addListener('request.error', function(Event $event) {        

            if ($event['response']->getStatusCode() == 401) {
                $newResponse = new Response($event['response']->getStatusCode());
                $event['response'] = $newResponse;
                $event->stopPropagation();
            }            
        });

        try {

            $client->setDefaultOption('query', $query_string);
            $request = $client->get($api_version . $test['method'], array(), isset($test['query'])?$test['query']:array());


          // Do something with Guzzle.
            $response = $request->send();   
            displayTest($request, $response);
        }
        catch (Guzzle\Http\Exception\ClientErrorResponseException $e) {

            $req = $e->getRequest();
            $resp =$e->getResponse();
            displayTest($req,$resp);
        }
        catch (Guzzle\Http\Exception\ServerErrorResponseException $e) {

            $req = $e->getRequest();
            $resp =$e->getResponse();
            displayTest($req,$resp);
        }
        catch (Guzzle\Http\Exception\BadResponseException $e) {

            $req = $e->getRequest();
            $resp =$e->getResponse();
            displayTest($req,$resp);
        }
        catch( Exception $e){
            echo "AGH!";
        }

        unset($client);
        $client=null;

    }

던져진 예외 유형에 대한 특정 catch 블록이 있어도 여전히 돌아오고 있습니다.

Fatal error: Uncaught exception 'Guzzle\Http\Exception\ClientErrorResponseException' with message 'Client error response [status code] 401 [reason phrase] Unauthorized [url]

예상대로 페이지의 모든 실행이 중지됩니다. BadResponseException catch를 추가하여 404를 올바르게 잡을 수 있었지만 500 또는 401 응답에 대해서는 작동하지 않는 것 같습니다. 누구든지 내가 어디로 잘못 가고 있는지 제안 할 수 있습니까?


3
이 코드가 네임 스페이스에 있습니까? 그렇다면 use예외를 지정 하지 않는 한 FQ 클래스를 명시 적으로 설명하기 위해 ''접두사를 붙여야 할 수 있습니다. 따라서, 예를 들어, '\ 목구멍 \ HTTP를 \ 예외 \ ClientErrorResponseException'
안토니 스털링

답변:


17

해당 try블록 에서 예외가 발생하면 최악의 시나리오에서 Exception잡히지 않은 모든 것을 포착해야합니다.

테스트의 첫 번째 부분이 Exception을 던지고이를 try블록에 래핑하는 것임을 고려하십시오 .


1
맞습니다. 예외를 던지는 try / catch 외부에 테스트가있었습니다. 어리석은 실수입니다. 도와 주셔서 감사합니다.
에릭

146

프로젝트에 따라 guzzle에 대한 예외를 비활성화해야 할 수도 있습니다. 때때로 코딩 규칙은 흐름 제어에 대한 예외를 허용하지 않습니다. 다음 과 같이 Guzzle 3에 대한 예외 비활성화 할 수 있습니다 .

$client = new \Guzzle\Http\Client($httpBase, array(
  'request.options' => array(
     'exceptions' => false,
   )
));

이것은 시간 초과와 같은 것에 대한 컬 예외를 비활성화하지는 않지만 이제 모든 상태 코드를 쉽게 얻을 수 있습니다.

$request = $client->get($uri);
$response = $request->send();
$statuscode = $response->getStatusCode();

확인하려면 유효한 코드가 있으면 다음과 같이 사용할 수 있습니다.

if ($statuscode > 300) {
  // Do some error handling
}

... 또는 모든 예상 코드를 더 잘 처리합니다.

if (200 === $statuscode) {
  // Do something
}
elseif (304 === $statuscode) {
  // Nothing to do
}
elseif (404 === $statuscode) {
  // Clean up DB or something like this
}
else {
  throw new MyException("Invalid response from api...");
}

Guzzle 5.3 용

$client = new \GuzzleHttp\Client(['defaults' => [ 'exceptions' => false ]] );

@mika 덕분에

Guzzle 6 용

$client = new \GuzzleHttp\Client(['http_errors' => false]);

10
break;-) 누락으로 인한 이상한 버그가 있었지만 동일한 방식으로 처리해야하는 여러 상태 코드가있는 경우 좋은 해결책이 될 것입니다. 내가 선호하는 if이유는 스위치가 지원하기 때문 ==입니다.
Trendfischer

을 (를) 언급 해 주셔서 감사합니다 request.options. 내 문제를 해결하고 제대로 찾아 볼 수있었습니다. :)
DanielM 2015 년

2
또는 Guzzle5.3에서 : $ client = new \ GuzzleHttp \ Client ([ 'defaults'=> [ 'exceptions'=> false]]);
미카

이것은 긴급한 프로젝트에서 내 베이컨을 구했습니다. Trendfischer와 SO 감사합니다!
Dan Barron

46

Guzzle 오류를 잡으려면 다음과 같이 할 수 있습니다.

try {
    $response = $client->get('/not_found.xml')->send();
} catch (Guzzle\Http\Exception\BadResponseException $e) {
    echo 'Uh oh! ' . $e->getMessage();
}

... 그러나 요청을 "로그"하거나 "재전송"하려면 다음과 같이 시도하십시오.

// Add custom error handling to any request created by this client
$client->getEventDispatcher()->addListener(
    'request.error', 
    function(Event $event) {

        //write log here ...

        if ($event['response']->getStatusCode() == 401) {

            // create new token and resend your request...
            $newRequest = $event['request']->clone();
            $newRequest->setHeader('X-Auth-Header', MyApplication::getNewAuthToken());
            $newResponse = $newRequest->send();

            // Set the response object of the request without firing more events
            $event['response'] = $newResponse;

            // You can also change the response and fire the normal chain of
            // events by calling $event['request']->setResponse($newResponse);

            // Stop other events from firing when you override 401 responses
            $event->stopPropagation();
        }

});

... 또는 "이벤트 전파를 중지"하려면 이벤트 리스너 (-255보다 높은 우선 순위)를 무시하고 이벤트 전파를 중지 할 수 있습니다.

$client->getEventDispatcher()->addListener('request.error', function(Event $event) {
if ($event['response']->getStatusCode() != 200) {
        // Stop other events from firing when you get stytus-code != 200
        $event->stopPropagation();
    }
});

다음과 같은 guzzle 오류를 방지하는 것이 좋습니다.

request.CRITICAL: Uncaught PHP Exception Guzzle\Http\Exception\ClientErrorResponseException: "Client error response

귀하의 응용 프로그램에서.


6
이것은 Guzzle 6에서는 더 이상 가능하지 않습니다. 미들웨어로이 작업을 수행하는 방법을 아십니까?
fnagel

30

제 경우 Exception에는 네임 스페이스 파일을 던지고 있었기 때문에 PHP는 My\Namespace\Exception예외를 전혀 포착하지 못했습니다.

catch (Exception $e)올바른 Exception클래스를 찾는 지 확인할 가치 가 있습니다.

그냥 시도 catch (\Exception $e)(그와 함께 \이)하고 작동되는지 확인합니다.


4
나는 처음에 같은 질문을 받았을 때이 오류로 스크롤했으면 좋았을 것입니다. 나를 위해 나는 오래된 Guzzle Exception 이름을 사용하고 있었고 루트 Namesapce에 없었기 때문에 일반 예외를 포착하지 못했습니다. 예외가 발생하기 전에 백 슬래시를 추가하면 일반 예외를 포착하여 더 구체적인 Guzzle 예외에서 내 이름 불일치 오류를 볼 수 있습니다. stackoverflow.com/a/7892917/2829359에 대한 의견을 참조하십시오 .
Carson Evans

이것은 내가 가진 정확한 문제였습니다. 좋은 답변
프라 사드 Rajapaksha


5

오래된 질문이지만 Guzzle은 예외 객체 내에 응답을 추가합니다. 그래서 간단한 try-catch를 GuzzleHttp\Exception\ClientException한 다음 getResponse해당 예외를 사용하여 400 레벨 오류를 확인하고 거기에서 계속하십시오.


2

내가 잡기되었다 GuzzleHttp\Exception\BadResponseException@dado이 제안되어있다. 하지만 어느 날 GuzzleHttp\Exception\ConnectException도메인 용 DNS를 사용할 수 없었습니다. 그래서 제 제안은 GuzzleHttp\Exception\ConnectExceptionDNS 오류에 대해서도 안전을 확보하는 것입니다.


, 및 GuzzleHttp\Exception\RequestException의 부모를 잡아야 할 것 같습니다 . ConnectExceptionBadResponseExceptionTooManyRedirectsException
Flame

1

Psr-7 Guzzle, Guzzle7 및 HTTPClient (laravel에서 제공하는 Guzzle HTTP 클라이언트 주변의 표현적이고 최소한의 API)에서 예외 처리에 대한 답변을 업데이트하고 싶습니다.

Guzzle7 (Guzzle 6에서도 동일한 작업)

RequestException을 사용하면 RequestException 은 요청을 전송하는 동안 발생할 수있는 모든 예외를 포착합니다.

try{
  $client = new \GuzzleHttp\Client(['headers' => ['Authorization' => 'Bearer ' . $token]]);
  
  $guzzleResponse = $client->get('/foobar');
  // or can use
  // $guzzleResponse = $client->request('GET', '/foobar')
    if ($guzzleResponse->getStatusCode() == 200) {
         $response = json_decode($guzzleResponse->getBody(),true);
         //perform your action with $response 
    } 
}
catch(\GuzzleHttp\Exception\RequestException $e){
   // you can catch here 400 response errors and 500 response errors
   // You can either use logs here use Illuminate\Support\Facades\Log;
   $error['error'] = $e->getMessage();
   $error['request'] = $e->getRequest();
   if($e->hasResponse()){
       if ($e->getResponse()->getStatusCode() == '400'){
           $error['response'] = $e->getResponse(); 
       }
   }
   Log::error('Error occurred in get request.', ['error' => $error]);
}catch(Exception $e){
   //other errors 
}

Psr7 Guzzle

use GuzzleHttp\Psr7;
use GuzzleHttp\Exception\RequestException;

try {
    $client->request('GET', '/foo');
} catch (RequestException $e) {
    $error['error'] = $e->getMessage();
    $error['request'] = Psr7\Message::toString($e->getRequest());
    if ($e->hasResponse()) {
        $error['response'] = Psr7\Message::toString($e->getResponse());
    }
    Log::error('Error occurred in get request.', ['error' => $error]);
}

HTTPClient의 경우

use Illuminate\Support\Facades\Http;
try{
    $response = Http::get('http://api.foo.com');
    if($response->successful()){
        $reply = $response->json();
    }
    if($response->failed()){
        if($response->clientError()){
            //catch all 400 exceptions
            Log::debug('client Error occurred in get request.');
            $response->throw();
        }
        if($response->serverError()){
            //catch all 500 exceptions
            Log::debug('server Error occurred in get request.');
            $response->throw();
        }
        
    }
 }catch(Exception $e){
     //catch the exception here
 }

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.