I'm having a difficult time catching a SoapClient authentication issue. When my code executes, Laravel declares it's throwing an ErrorException but I can't seem to catch it no matter what code I use. I'm tagging Laravel in case there's some magic going on somewhere I don't know about because App::error() will trigger on this error still.
try {
$client = new SoapClient(
$this->serviceUrl . $this->clients[$clientName],
array(
'login' => $this->username,
'password' => $this->password,
'exceptions' => true,
)
);
} catch (SoapFault $e) {
die('soapfault never fires!');
} catch (Exception $e) {
die('exception won\t t');
} catch (ErrorException $e) {
die('error exception also doesn\'t error');
}
According to Laravel an ErrorException is being thrown but the above code doesn't catch it.
ErrorException
SoapClient::SoapClient(https://control.akamai.com/nmrws/services/RealtimeReports?wsdl) [<a href='soapclient.soapclient'>soapclient.soapclient</a>]: failed to open stream: HTTP request failed! HTTP/1.1 401 Unauthorized
Add backslash before exception types (e.g. Exception becomes \Exception).
They belong to global namespace. Your code tries to catch exceptions in currently used namespace which doesn't have to be the same as global.
Related
I need to connect to an API so I write a function:
try {
$res4 = $client3->post('https://api.example.co.uk/Book', [
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ajhsdbjhasdbasdbasd',
],
'json' => [
'custFirstName' => $FirstName,
'custLastName' => $Surname,
'custPhone' => $Mobile,
'custEmail' => $Email,
]
]);
} catch (GuzzleHttp\Exception\ClientException $e) {
$response = $e->getResponse();
$result = json_decode($response->getBody()->getContents());
$item->update(['status' => 'Problems at step3']);
Mail::raw('Problem at STEP 3', function ($message) use ($serial) {
$message->from('asd.asd#gmail.com', 'asd.asd#gmail.com');
$message->subject('We got a problem etc.');
$message->to('john.smith#gmail.com');
});
}
As you can see I need to make a call to API but in the case when API is down I write catch functions.
But now when API is down and API return '500 Internal Error' this function is just crashed ...
My question is why catch dont handle it?
How I can handle errors - when API is down or bad request... WHy catch{} doesn't work?
UPDATE: here is my laravel.log
[2018-10-25 14:51:04] local.ERROR: GuzzleHttp\Exception\ServerException: Server error: `POST https://api.example.co.uk/Book` resulted in a `500 Internal Server Error` response:
{"message":"An error has occured. Please contact support."}
in /home/public_html/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php:107
Stack trace:
#0 /home/public_html/vendor/guzzlehttp/guzzle/src/Middleware.php(65): GuzzleHttp\Exception\RequestException::create(Object(GuzzleHttp\Psr7\Request), Object(GuzzleHttp\Psr7\Response))
#1 /home/public_html/vendor/guzzlehttp/promises/src/Promise.php(203): GuzzleHttp\Middleware::GuzzleHttp\{closure}(Object(GuzzleHttp\Psr7\Response))
The problem are namespaces here, instead of:
} catch (GuzzleHttp\Exception\ClientException $e) {
you should rather use:
} catch (\GuzzleHttp\Exception\ClientException $e) {
Otherwise PHP assumes that class is in current namespacase, so in fact when you used GuzzleHttp\Exception\ClientException in fact you probably used App\Http\Controllers\GuzzleHttp\Exception\ClientException and such exception obviously won't be thrown by Guzzle.
The exception that is fired is a ServerException instance, and catch block tries to catch ClientException.
} catch (GuzzleHttp\Exception\ServerException $e) {
in your app/exceptions/handler.php file, update the render method like this one.
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception) {
if ($exception instanceof \GuzzleHttp\Exception\ClientException) {
return your_response();
}
return parent::render($request, $exception);
}
This approach worked for me.
The issue is you're trying to catch an exception, when you should actually be trying to catch an error. In your catch block try using \Error instead of \GuzzleHttp\Exception\ClientException.
The app is built on the MVC pattern, so my controllers are separate from my models.
I am using two payment gateways, Stripe and PayPal and have moved the API code into a model called Payment_Model.php.
Both functions have huge try/catch blocks that throw all manner of errors when a payment fails which is a good thing for me, not so for a customer...
Here is a try catch block example
try {
Stripe::setApiKey($this->config->item('stripe_secret_key'));
$customer = Customer::create([
'email' => 'customer#example.com',
'source' => $this->input->post('stripe_token'),
]);
$charge = Charge::create([
'customer' => $customer->id,
'amount' => $option->price,
'currency' => 'eur',
"description" => "Demo Transaction", // #TODO
]);
} catch (Exception $e) {
} catch (Stripe_CardError $e) {
throw new Exception($e);
} catch (Stripe_InvalidRequestError $e) {
throw new Exception($e);
} catch (Stripe_AuthenticationError $e) {
throw new Exception($e);
} catch (Stripe_ApiConnectionError $e) {
throw new Exception($e);
} catch (Stripe_Error $e) {
throw new Exception($e);
} catch (Exception $e) {
throw new Exception($e);
}
I don't want to display these errors or exceptions in my production environment... instead I would like to replace throw new Exception($e) with false so that I can call the model function in my controller and if something goes wrong I can redirect the user to a decent error page...
So my question is this:
Can I return a boolean IF something bad is caught so that I can either redirect to a success page or an error page in my controller? Or am I missing the point of using exceptions?
Am new to Slim framework and struggling to set up exception handling in slim, my requirement is to redirect to an error page when something unexpected happens in my code.
Tried this code
$smartView= new \Slim\Views\Smarty();
$app = new \Slim\Slim(array(
'debug' => false,
'view' => $smartView,
'templates.path' => '../templates/',
));
$app->error(function ( Exception $e ) use ($app) {
echo "my exception print here : " . $e;
});
in my index.php file, but slim slim still calling its default exception handler.
This is my router call
$app->get('/game', function () use ($app) {
try{
$facebook = new Facebook(array(
'appId' => appid,
'secret' =>appsecret,
'cookie' => true,
'allowSignedRequest' => true
));
$oStuff = new models\User ();
$oStuff->fbLogin($facebook); // To get User details and game select
}
catch (\Exception $e) {
//echo 'Caught exception: ', $e->getMessage(), "\n";
echo $e;
echo "catch exception";
}
});
this is my function having some errors
public function fbLogin($facebook)
{
$app = \Slim\Slim::getInstance();
$user = $facebook->getUser() // here is syntax error so i need to get it in my exception
}
Please help me to solve this issue, thanks in advance
If you are catching yourself an Exception like you do with you catch statement you won't let Slim handle the Exception for you so you will never enter your custom error method.
You can see official statement about error handling here and also check the code source here at line 1405.
So you have 2 choices here :
1) not try/catch your exception and let all exceptions be handled by Slim Framework
2) try/catch and throw a new Exception in your catch ... (not sure if its very useful)
Also consider the debug flag when bootstrapping the app, if true you will have a complete stacktrace of your exception, if false you need to display something nice to user in your "error" method.
You also can write your own log write so you will log Exception by yourself. More infos here
I'm trying to catch exceptions from a set of tests I'm running on an API I'm developing and I'm using Guzzle to consume the API methods. I've got the tests wrapped in a try/catch block but it is still throwing unhandled exception errors. Adding an event listener as described in their docs doesn't seem to do anything. I need to be able to retrieve the responses that have HTTP codes of 500, 401, 400, in fact anything that isn't 200 as the system will set the most appropriate code based on the result of the call if it didn't work.
Current code example
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;
}
Even with the specific catch block for the thrown exception type I am still getting back
Fatal error: Uncaught exception 'Guzzle\Http\Exception\ClientErrorResponseException' with message 'Client error response [status code] 401 [reason phrase] Unauthorized [url]
and all execution on the page stops, as you'd expect. The addition of the BadResponseException catch allowed me to catch 404s correctly, but this doesn't seem to work for 500 or 401 responses. Can anyone suggest where I am going wrong please.
Depending on your project, disabling exceptions for guzzle might be necessary. Sometimes coding rules disallow exceptions for flow control. You can disable exceptions for Guzzle 3 like this:
$client = new \Guzzle\Http\Client($httpBase, array(
'request.options' => array(
'exceptions' => false,
)
));
This does not disable curl exceptions for something like timeouts, but now you can get every status code easily:
$request = $client->get($uri);
$response = $request->send();
$statuscode = $response->getStatusCode();
To check, if you got a valid code, you can use something like this:
if ($statuscode > 300) {
// Do some error handling
}
... or better handle all expected codes:
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...");
}
For Guzzle 5.3
$client = new \GuzzleHttp\Client(['defaults' => [ 'exceptions' => false ]] );
Thanks to #mika
For Guzzle 6
$client = new \GuzzleHttp\Client(['http_errors' => false]);
To catch Guzzle errors you can do something like this:
try {
$response = $client->get('/not_found.xml')->send();
} catch (Guzzle\Http\Exception\BadResponseException $e) {
echo 'Uh oh! ' . $e->getMessage();
}
... but, to be able to "log" or "resend" your request try something like this:
// 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();
}
});
... or if you want to "stop event propagation" you can overridde event listener (with a higher priority than -255) and simply stop event propagation.
$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();
}
});
thats a good idea to prevent guzzle errors like:
request.CRITICAL: Uncaught PHP Exception Guzzle\Http\Exception\ClientErrorResponseException: "Client error response
in your application.
In my case I was throwing Exception on a namespaced file, so php tried to catch My\Namespace\Exception therefore not catching any exceptions at all.
Worth checking if catch (Exception $e) is finding the right Exception class.
Just try catch (\Exception $e) (with that \ there) and see if it works.
If the Exception is being thrown in that try block then at worst case scenario Exception should be catching anything uncaught.
Consider that the first part of the test is throwing the Exception and wrap that in the try block as well.
You need to add a extra parameter with http_errors => false
$request = $client->get($url, ['http_errors' => false]);
I want to update the answer for exception handling in Psr-7 Guzzle, Guzzle7 and HTTPClient(expressive, minimal API around the Guzzle HTTP client provided by laravel).
Guzzle7 (same works for Guzzle 6 as well)
Using RequestException, RequestException catches any exception that can be thrown while transferring requests.
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]);
}
For 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
}
Old question, but Guzzle adds the response within the exception object. So a simple try-catch on GuzzleHttp\Exception\ClientException and then using getResponse on that exception to see what 400-level error and continuing from there.
I was catching GuzzleHttp\Exception\BadResponseException as #dado is suggesting. But one day I got GuzzleHttp\Exception\ConnectException when DNS for domain wasn't available.
So my suggestion is - catch GuzzleHttp\Exception\ConnectException to be safe about DNS errors as well.
If you are using the latest version say 6^ and you have a JSON parameter, you can add 'http_errors' => false to the array together with the JSON as seen below
I was looking out for away to do this i.e with my JSON in there but couldn't find a straight answer.
I'm using the Facebook PHP API, and around 1 time in 40 it dumps this exception on my webapp:
Uncaught CurlException: 56: SSL read:
error:00000000:lib(0):func(0):reason(0),
errno 104 thrown in ... on line 638
I'm not looking for a solution to what's causing the exception (already working on that), but for now I'd like to change it from dumping the exception on the page to either telling the user to refresh the page or refreshing the page automatically.
The exception is being thrown in this file: https://github.com/facebook/php-sdk/blob/master/src/facebook.php
This is the code I'd like to temporarily change to a refresh / refresh instruction:
if (curl_errno($ch) == 60) { // CURLE_SSL_CACERT
self::errorLog('Invalid or no certificate authority found, using bundled information');
curl_setopt($ch, CURLOPT_CAINFO,
dirname(__FILE__) . '/fb_ca_chain_bundle.crt');
$result = curl_exec($ch);
}
if ($result === false) {
$e = new FacebookApiException(array(
'error_code' => curl_errno($ch),
'error' => array(
'message' => curl_error($ch),
'type' => 'CurlException',
),
));
curl_close($ch);
throw $e;
}
You could use TRY .. CATCH to catch CurlException, or FacebookApiException there.
Or use set_exception_handler to catch any uncaught exception.
As strauberry said, you can stop throwing the exception. If the exception needs to be throw there, you can put the code that call this inside a try-catch and deal with the exception as you want.
Another option is to use the function set_exception_handler. This function will be called when an exception is thrown but nothing catch it.
You throw the Exception $e in the last line which causes the dumping. Instead you could do something like
echo "ERROR: " . $e->getMessage();