从Guzzle捕获异常


79

我正在尝试从正在开发的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'
安东尼英镑

Answers:


17

如果在该try块中引发了异常,那么在最坏的情况下,Exception应该捕获未捕获的任何东西。

考虑到测试的第一部分抛出了Exception,并且也将其包装在try块中。


1
您是正确的,在try / catch之外有一个测试引发了异常。愚蠢的错误,感谢您的帮助。
埃里克

144

根据您的项目,可能有必要禁用guzzle例外。有时,编码规则会禁止流控制例外。您可以像这样禁用Guzzle 3的异常:

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

这不会为超时等事件禁用curl异常,但是现在您可以轻松获取每个状态代码:

$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...");
}

狂饮5.3

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

感谢@mika

食尸鬼6

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

10
曾经有一个由于丢失而引起的奇怪错误break;-)但是,如果您必须以相同的方式处理多个状态代码,那将是一个很好的解决方案。我更喜欢if,因为开关只是支持==
Trendfischer

感谢您的提及request.options。解决了我的问题,并节省了我的查找时间。:)
DanielM

2
或在Guzzle5.3中:$ client = new \ GuzzleHttp \ Client(['defaults'=> ['exceptions'=> false]]);
mika

这节省了我的培根上的紧急项目。感谢Trendfischer和SO!
丹·巴伦

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

多数民众赞成在防止像这样的大嘴小错误的好主意:

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

在您的应用程序中。


6
在Guzzle 6中将不再可能。任何想法如何使用中间件来实现?
fnagel '17

30

就我而言,我抛出Exception的是命名空间文件,因此php试图捕获,My\Namespace\Exception因此根本不捕获任何异常。

值得检查是否catch (Exception $e)找到合适的Exception班级。

只需尝试一下catch (\Exception $e)(在\那里),看看它是否有效。


4
我希望我第一次有同样的问题时已经向下滚动到此错误。对我来说,我使用的是过时的Guzzle异常名称,而没有捕获通用的异常,因为我不在根Namesapce上。在Exception开始捕获通用的Exception之前添加反斜杠,使我可以在更具体的Guzzle Exceptions上看到我的姓名不匹配错误。请参阅stackoverflow.com/a/7892917/2829359上的评论。
卡森·埃文斯

这也是我遇到的确切问题。好答案
Prasad Rajapaksha


5

旧问题,但是Guzzle在异常对象中添加了响应。因此,简单地尝试捕获GuzzleHttp\Exception\ClientException然后getResponse在该异常上使用即可查看什么400级错误并从那里继续。


2

我正在GuzzleHttp\Exception\BadResponseException按@dado的建议进行捕捉。但是有一天我没有GuzzleHttp\Exception\ConnectException域名DNS。所以我的建议是-GuzzleHttp\Exception\ConnectException还要注意DNS错误。


听起来像是你应该抓住GuzzleHttp\Exception\RequestException这是父ConnectExceptionBadResponseExceptionTooManyRedirectsException
火焰

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食尸鬼

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
 }

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.