I am using Symfony HTTP client in an event subscriber and I set timeout to 0.1. The problem is I want the code to skip the HTTP request if took more than 0.1 second and it timedout and meanwhile no error/exception needs to be thrown. I tried try and catch but it does not work.
public function checkToken()
{
try {
$response = $this->client->request('POST','url/of/api', [
'json' => ['token' => $_ENV['USER_TOKEN']],
'timeout' => 0.1,
]);
}catch (\Exception $e){
}
}
Why it can not be handled via try and catch?
Its because responses are lazy in Symfony.
As this document mentioned:
https://symfony.com/doc/current/http_client.html#handling-exceptions
A solution is to call getContent method in try section.
private $client;
private $user_data;
public function __construct(HttpClientInterface $client)
{
$this->client = $client;
}
public function checkToken(RequestEvent $event)
{
try {
$response = $this->client->request('POST', 'url/of/api', [
'json' => ['token' => $_ENV['USER_TOKEN']],
'timeout' => 0.1,
]);
$this->user_data = $response->getContent();
} catch (\Exception $e) {
echo $e->getMessage();
}
}
Related
I'm new to Twilio. I'm using Twilio for phone verification of my app. I'm using Laravel 5.5 for the backend & APIs. I've successfully sent the SMS to phone. I'm getting the call from Twilio but it says an application error. It doesn't read what I want to hear.
Below I'm giving every detail of my code.
Used composer require twilio/sdk for Twilio.
This is my Controller.
use Twilio\Rest\Client;
use Twilio\Twiml;
class AppUserController extends Controller{
private $account_sid;
private $auth_token;
private $twilio_number;
public function __construct(){
$this->account_sid = Config::get('envvalue.account_sid');
$this->auth_token = Config::get('envvalue.auth_token');
$this->twilio_number = Config::get('envvalue.twilio_number');
}
public function reVerification(Request $request){
$client = new Client($this->account_sid, $this->auth_token);
try {
$client->account->calls->create(
$receiverNumber,
$this->twilio_number,
array(
"url" => "http://demo.bitcanny.com/marine-admin/public/api/twiml/"
)
);
return response()->json([
'success' => true,
'statusCode' => '200',
'message' => 'Otp send again'
], 200);
}
catch (Exception $e) {
return $e->getMessage();
}
}
public function twiml(){
// A message for Twilio's TTS engine to repeat
$sayMessage = 'Hello.';
$twiml = new Twiml();
$twiml->say($sayMessage);
$response = Response::make($twiml, 200);
$response->header('Content-Type', 'text/xml');
return $response;
}
}
I found the solution. There's a silly mistake. I didn't use Response in the header of my controller.
use Response;
i am sending otp using twilio,laravel, message is working now, but i want to set exception for if message is not delivered etc i have tried like
public function send_otp()
{
try {
$account_sid = env('TWILIO_ACCOUNT_SID');
$auth_token = env('TWILIO_AUTH_TOKEN');
$number=Auth::user()->user_phone;
$client = new Client($account_sid, $auth_token);
$messages = $client->messages->create($number, array(
'From' => '+12533368077',
'Body' => Auth::user()->user_otp,
));
dd($messages);
//return $messages;
//throw new Exception();
} catch (Exception $e) {
return response()->json(['error' => true,'message'=>'Something went wrong'],200);
}
}
can you please help me with this
After setting env data did you clear cache?
php artisan config:cache
If you want to handle error - laravel has special logic for that. You need to just to catch that error and then make action, it is simple:
https://laravel.com/docs/5.6/errors
public function render($request, Exception $exception)
{
if ($exception instanceof CustomException) {
return response()->view('errors.custom', [], 500);
}
return parent::render($request, $exception);
}
Is there any way to mock response and request in Guzzle?
I have a class which sends some request and I want to test.
In Guzzle doc I found a way how can I mock response and request separately. But how can I combine them?
Because, If use history stack, guzzle trying to send a real request.
And visa verse, when I mock response handler can't test request.
class MyClass {
public function __construct($guzzleClient) {
$this->client = $guzzleClient;
}
public function registerUser($name, $lang)
{
$body = ['name' => $name, 'lang' = $lang, 'state' => 'online'];
$response = $this->sendRequest('PUT', '/users', ['body' => $body];
return $response->getStatusCode() == 201;
}
protected function sendRequest($method, $resource, array $options = [])
{
try {
$response = $this->client->request($method, $resource, $options);
} catch (BadResponseException $e) {
$response = $e->getResponse();
}
$this->response = $response;
return $response;
}
}
Test:
class MyClassTest {
//....
public function testRegisterUser()
{
$guzzleMock = new \GuzzleHttp\Handler\MockHandler([
new \GuzzleHttp\Psr7\Response(201, [], 'user created response'),
]);
$guzzleClient = new \GuzzleHttp\Client(['handler' => $guzzleMock]);
$myClass = new MyClass($guzzleClient);
/**
* But how can I check that request contains all fields that I put in the body? Or if I add some extra header?
*/
$this->assertTrue($myClass->registerUser('John Doe', 'en'));
}
//...
}
#Alex Blex was very close.
Solution:
$container = [];
$history = \GuzzleHttp\Middleware::history($container);
$guzzleMock = new \GuzzleHttp\Handler\MockHandler([
new \GuzzleHttp\Psr7\Response(201, [], 'user created response'),
]);
$stack = \GuzzleHttp\HandlerStack::create($guzzleMock);
$stack->push($history);
$guzzleClient = new \GuzzleHttp\Client(['handler' => $stack]);
First of all, you don't mock requests. The requests are the real ones you are going to use in production. The mock handler is actually a stack, so you can push multiple handlers there:
$container = [];
$history = \GuzzleHttp\Middleware::history($container);
$stack = \GuzzleHttp\Handler\MockHandler::createWithMiddleware([
new \GuzzleHttp\Psr7\Response(201, [], 'user created response'),
]);
$stack->push($history);
$guzzleClient = new \GuzzleHttp\Client(['handler' => $stack]);
After you run your tests, $container will have all transactions for you to assert. In your particular test - a single transaction. You are interested in $container[0]['request'], since $container[0]['response'] will contain your canned response, so there is nothing to assert really.
I'm using laravel and I have setup the abstract class method to get response from the various APIs I'm calling. But if the API url is unreachable then it throws an exception. I know I'm missing something. Any help would be great for me.
$offers = [];
try {
$appUrl = parse_url($this->apiUrl);
// Call Api using Guzzle
$client = new Client('' . $appUrl['scheme'] . '://' . $appUrl['host'] . '' . $appUrl['path']);
if ($appUrl['scheme'] == 'https') //If https then disable ssl certificate
$client->setDefaultOption('verify', false);
$request = $client->get('?' . $appUrl['query']);
$response = $request->send();
if ($response->getStatusCode() == 200) {
$offers = json_decode($response->getBody(), true);
}
} catch (ClientErrorResponseException $e) {
Log::info("Client error :" . $e->getResponse()->getBody(true));
} catch (ServerErrorResponseException $e) {
Log::info("Server error" . $e->getResponse()->getBody(true));
} catch (BadResponseException $e) {
Log::info("BadResponse error" . $e->getResponse()->getBody(true));
} catch (\Exception $e) {
Log::info("Err" . $e->getMessage());
}
return $offers;
you should set the guzzehttp client with option 'http_errors' => false,
the example code should be like this, document:guzzlehttp client http-error option explain
Set to false to disable throwing exceptions on an HTTP protocol errors (i.e., 4xx and 5xx responses). Exceptions are thrown by default when HTTP protocol errors are encountered.
$client->request('GET', '/status/500');
// Throws a GuzzleHttp\Exception\ServerException
$res = $client->request('GET', '/status/500', ['http_errors' => false]);
echo $res->getStatusCode();
// 500
$this->client = new Client([
'cookies' => true,
'headers' => $header_params,
'base_uri' => $this->base_url,
'http_errors' => false
]);
$response = $this->client->request('GET', '/');
if ($code = $response->getStatusCode() == 200) {
try {
// do danger dom things in here
}catch (/Exception $e){
//catch
}
}
These exceptions are not defined in guzzle officially.
These Exceptions are defined in AWS SDK for PHP.
For official Guzzle you may just do following.
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ClientException;
....
try {
$response = $this->client->request($method, $url, [
'headers' => $headers,
'form_params' => $form_parameters,
]);
$body = (string)$response->getBody();
} catch (ClientException $e) {
// Do some thing here...
} catch (RequestException $e) {
// Do some thing here...
} catch (\Exception $e) {
// Do some thing here...
}
you could create your own exception
class CustomException extends Exception
{
}
next you throws your own exception from abstract class guzzle exception
catch(ConnectException $ConnectException)
{
throw new CustomException("Api excception ConnectException",1001);
}
and then handdle in in global exception handdler render methos
if($exception instanceof CustomException)
{
dd($exception);
}
Not sure how you declared those exceptions and which Guzzle version are you using but in official documentation those exceptions don't exist.
In your case you are probally missing GuzzleHttp\Exception\ConnectException.
I have this simple REST api, done in Slim,
<?php
require '../vendor/autoload.php';
function getDB()
{
$dsn = 'sqlite:/home/branchito/personal-projects/slim3-REST/database.sqlite3';
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
try {
$dbh = new PDO($dsn);
foreach ($options as $k => $v)
$dbh->setAttribute($k, $v);
return $dbh;
}
catch (PDOException $e) {
$error = $e->getMessage();
}
}
$app = new \Slim\App();
$app->get('/', function($request, $response) {
$response->write('Bienvenidos a Slim 3 API');
return $response;
});
$app->get('/getScore/{id:\d+}', function($request, $response, $args) {
try {
$db = getDB();
$stmt = $db->prepare("SELECT * FROM students
WHERE student_id = :id
");
$stmt->bindParam(':id', $args['id'], PDO::PARAM_INT);
$stmt->execute();
$student = $stmt->fetch(PDO::FETCH_OBJ);
if($student) {
$response->withHeader('Content-Type', 'application/json');
$response->write(json_encode($student));
} else { throw new PDOException('No records found');}
} catch (PDOException $e) {
$response->withStatus(404);
$err = '{"error": {"text": "'.$e->getMessage().'"}}';
$response->write($err);
}
return $response;
});
$app->run();
however, I can't get browser to send me application/json content type, it
always sends text/html? What I am doing wrong?
EDIT:
Ok, after two hours of hitting the head against the wall, I stumbled upon this answer:
https://github.com/slimphp/Slim/issues/1535 (at the bottom of a page)
which explains what happens, appears that response object is immutable and
as such it must be returned or reassigned if you want to return it after
while.
So, instead of this:
if($student) {
$response->withHeader('Content-Type', 'application/json');
$response->write(json_encode($student));
return $response;
} else { throw new PDOException('No records found');}
Do like this:
if($student) {
return $response->withStatus(200)
->withHeader('Content-Type', 'application/json')
->write(json_encode($student));
} else { throw new PDOException('No records found');}
And all is well and good.
For V3, withJson() is available.
So you can do something like:
return $response->withStatus(200)
->withJson(array($request->getAttribute("route")
->getArgument("someParameter")));
Note: Make sure you return the $response because if you forget, the response will still come out but it will not be application/json.
For V3, the simplest method as per the Slim docs is:
$data = array('name' => 'Rob', 'age' => 40);
return $response->withJson($data, 201);
This automatically sets the Content-Type to application/json;charset=utf-8 and lets you set a HTTP status code too (defaults to 200 if omitted).
You can also use:
$response = $response->withHeader('Content-Type', 'application/json');
$response->write(json_encode($student));
return $response;
because withHeader return new response object. That way you have more then one write and code between.