Guzzle how to handle RequestException when host is offline - php

I am using Guzzle inside the laravel framework but when the host is offline I get a exception instead of a statuscode which you can get with getStatusCode(). I have now made a try catch around the request, but outside this method I have a method which checks on the statuscode. My question is how can I return in the catch the correct response so I could call outside this method getStatusCode().
My code to make the request looks like this:
public function makeRequest($method, $requestUrl, $queryParams = [])
{
try{
$client = new Client(['http_errors' => false]);
return $client->request($method, $requestUrl, [
'query' => $queryParams
]);
}catch(RequestException $exception){
LOG::info($exception->getMessage());
return $exception->getResponse();
}
}

You idea (always return Response object from the method) doesn't work by design. I mean that you see it already, there is no Response object is some cases (when a connection to the host cannot be established, for example, so HTTP flow doesn't even start in this case, that why you don't have any HTTP status code).
IMO the best way to make your code aware of the exception. Don't try to handle it inside your makeRequest(), just let it flow further, to the point where you actually can handle it.

Related

How can multiple method calls be chained on slim's response object?

I want to chain multiple method calls on slims's $response object, but if i do i get an error ( status 500) and nothing happens.
This might aswell be a lack of basic PHP knowledge, i am not very experienced in PHP and it is my first time working with slim, or any serverside / API framework for that matter.
I have tried flipping arround the order of the calls, and doing them in different lines but to no awail. The long term goel of this, to build an API for an update application. So i will have to handle get requests with multiple parameters, evaluate them and depending on the results, do and return deffirent responses.
// this one fails, i set the status to 900 on purpose just to see what happens
$app->get('/', function (Request $request, Response $response, array $args) {
$response->getBody()->write("Slim main page")->withStatus(900);
return $response;
});
The first example does give me an 500 error on the network tab. This would suggest some type of syntax error i guess? If i alter this route a little bit to look like this :
# this one works fine, except the status code setting gets ignored, but why?
$app->get('/', function (Request $request, Response $response, array $args) {
$response->write("Slim main page")->withStatus(900);
return $response;
});
things alsmost work out, but the status code is not set for some reason.
I would expect the first one, to return the string "slim main page" with the status code 900. Even if i use a non made up status code, this setting gets ignored.
The second code block ist just an alteration for testing purposes.
I am pretty sure this a newby thing but i am really lost here, so any advice, or some fool proof articles / docs besides the slim docs are appreciated.
The write method returns the number of bytes written to the stream (and not the new response object). Try this:
$app->get('/', function (Request $request, Response $response, array $args = []) {
$response->getBody()->write('Slim main page');
$response = $response->withStatus(200);
return $response;
});
Notice 1: Enable the error details on dev: 'displayErrorDetails' => true
Notice 2: The HTTP code 900 is an invalid status code for Slim and will throw the following execption.
Type: InvalidArgumentException
Message: Invalid HTTP status code
File: vendor/slim/slim/Slim/Http/Response.php
Line: 228

How to return/terminate symfony2 response in function other than the requested action?

I'm building a RESTful API using Symfony2, FOSRestBundle and an OAuth 2 server library.
For any given request, there are a number of possible responses and status codes that I can return to the client.
Take for example the process of getting a user:
<?php
class UserController extends CustomBaseController {
/**
* Get a user
* #Get("/users", name="users_get")
*/
public function getAction(Request $request) {
// Ensure a valid access token is present
$this->validAccessTokenOrExit();
$user = $this->getUser();
return $this->handleView($this->view($user, 200));
}
}
Ideally I would like to have the validAccessTokenOrExit() function terminate the request and return the 401 status code with an appropriate message. This means I can handle authentication in this function and reuse it in several actions across the API.
However I can't seem to find a way of terminating the response from another function, and I always have to return a Response from the action called.
Are there any clean solutions to accomplish this or something similar?
If you throw an exception that has the interface Symfony\Component\HttpKernel\Exception\HttpExceptionInterface (Symfony\Component\HttpKernel\Exception\HttpException for example) with the status code set (first parameter as 401 for HttpException) it will be handled by the HttpKernel in the way that you are expecting.
For example..
throw new HttpException(401, 'Something went wrong');
.. will be turned into a response with 401 as the status code.
You can use $this->createAccessDeniedException('message') within a controller.
This will create AccessDeniedException, terminate the current request and return a 403 with your message.
I did faced this situation and for time being I call the terminating function with return. Return false from function when valid.
if($r=$this->validAccessTokenOrExit()){
return $r;
}
Alternatively I use following two methods for redirecting or rendering a view form another function and terminating flow.
Redirection:
header('location:'. $this->generateUrl('deals_product_view', array('id' => 1)));
exit;
Rendering a view:
$response = new Response();
$response->setContent($this->renderView($view_file, $params ));
$response->send();
exit;

How to catch laravel controller exceptions

I'm new to Laravel (we're using 5.0 at work). Right now, when we respond to an API request in a Controller, we are rewriting the same code over and over to respond to unauthorized actions. For example,
public function getUsers(){
if (Entrust::can('users.view')){
$users = Users::get();
return response()->done($users, 200);
} else {
return response()->unauthorized('users.view');
}
}
It gets more and more complicated if we have different permissions that can allow an API request to succeed.
I'd like to simply throw an exception of some sort if the user cannot perform the API request. For example,
public function getUsers(){
require('users.view'); // throws an UnauthorizedException if current user doesn't have 'users.view' permission
$users = User::get();
return response()->done($users, 200);
}
public function someOtherMethod(){
if (!Entrust::can('permission1') && !Entrust::can('permission2')){
throw new UnauthorizedException(['permission1', 'permission2']);
}
// some other stuff
}
But I don't know what code calls the API function, nor where to wrap that call in a try/catch. It's easy enough to code the UnauthorizedException, and easy to transform it into json, but where do I put the handler? As I said, I'm new to Laravel, and I don't know how it handles these exceptions.
Ideally, whatever solution I find, I'd like to extend it to other exceptions so we can have consistent json responses based on common exceptions.
Instead of repeating your code, take a look at implementing the authorization check with Middleware.

Laravel Ajax validation

I understand how to validate requests by type-hinting the class name in the controller method. However for Ajax requests, According to the documentation, I should validate data in the controller, because using a validator class will redirect rather than send a response.
The main part I'm looking at is this:
If the incoming request was an AJAX request, no redirect will be
generated. Instead, an HTTP response with a 422 status code will be
returned to the browser containing a JSON representation of the validation errors.
However, my controller is as follows:
public function update(App\Permission $permission, Request $request)
{
$this->validate($request, [
'permission_description' => 'required|string'
]);
...
}
And I can't for the life of me get it to respond with JSON. The documentation states that if it fails, it throws an Illuminate\Contracts\Validation\ValidationException exception, but I can't catch it.
Whenever it fails, it always redirects back to the edit page. Obviously I don't want this, I want the json response.
I have just tried "manually writing it out" with the whole $v = Validator::make($request->all(), ...); which does work, but what's the point in using the $this->validate() way if it doesn't work?
Does the $this->validate() method just not work with AJAX and I have to write it the long way each time? Am I doing something wrong?!
Below is what I've tried:
public function update(App\Permission $permission, UpdatePermissionRequest $request)
{
/** Redirects rather than returns JSON if the validation fails **/
}
----------------------------------
public function update(App\Permission $permission, Request $request)
{
$this->validate($request, [
'permission_description' => 'required|string'
]);
/** AND I've also tried: **/
try {
$this->validate($request, ['permission_description' => 'required|string']);
} catch (\Illuminate\Contracts\Validation\ValidationException $e {
echo $e; /** Echoing for debug reasons **/
exit;
}
...
/** Still redirects the browser, even if it is an AJAX request **/
}
-----------------------------------------
use Validator;
...
public function update(App\Permission $permission, Request $request)
{
$v = Validator::make($request->all(), [
'permission_description' => 'required|string'
]);
if($v->fails())
{
return response()->json(['reply' => false]);
}
/** Works **/
}
UPDATE
The documentation is incorrect. It states that the $this->validate() method throws a Illuminate\Contracts\Validation\ValidationException but it doesn't. It throws a Illuminate\Http\Exception\HttpResponseException exception.
Simply telling that you want json in the header should also fix this. Laravel checks if the request is ajax of if json is requested.
if ($this->ajax() || $this->wantsJson())
{
return new JsonResponse($errors, 422);
}
Solution:
Add header
Accept: application/json
Your statement that the docs say it is best to validate AJAX requests in the controller is simply incorrect.
If you scroll a bit further down from what you linked - you'll see this under the FormValidation section
If validation fails, a redirect response will be generated to send the
user back to their previous location. The errors will also be flashed
to the session so they are available for display. If the request was
an AJAX request, a HTTP response with a 422 status code will be
returned to the user including a JSON representation of the validation
errors.
In other words - there is no reason you cannot do this in a simple FormRequest and simply your code significantly. It will automatically handle the fact it is an AJAX call and return the appropriate HTTP responses.
I do this all the time in my L5 apps - it works flawlessly.
Ok, so it looks like there were 2 contributing factors.
The reason it was redirecting instead of responding with JSON is because the X-Requested-With field wasn't set on the AJAX request. This the AJAX wrapper for the application was setup to deal with Cross Domain requests, which seems to strip out the X-Requested-With field, which makes the ultimate request look non-ajax to the server - hence the redirection.
The reason why it wasn't catching the Illuminate\Contracts\Validation\ValidationException exception is because that exception is not thrown. If you open up Illuminate\Foundation\Validation\ValidatesRequest.php, and search for the function throwValidationException(), it actually throws a HttpResponseException instead. I attempted to catch the HttpResponseException and it was caught successfully - I assume the documentation is wrong.
The solution was to either remove the cross domain attributes on the ajax request, add the X-Requested-With header manually, as per the answer in this post. This would make the application see that it was an AJAX request.
And if you wanted to manually catch the exception, you need to catch the HttpResponseException, not the ValidationException.

Exception handling with phpunit and Silex

I am using a route in Silex to delete an object from the database. If an object does not exist, a 404 error should be thrown. This works fine in the browser, and the response is received accordingly.
This is my source:
$app->delete("/{entity}/{id}", function(\Silex\Application $app, HttpFoundation\Request $request, $entity, $id) {
// some prep code is here
$deleteObject = $this->em->getRepository($entityClass)->find($id);
if (empty($deleteObject))
$app->abort(404, "$ucEntity with ID $id not found");
// other code comes here...
}
This is my test case:
// deleting the same object again should not work
$client->request("DELETE", "/ccrud/channel/$id");
$this->assertTrue($response->getStatusCode() == 404);
Now phpunit fails with the following error:
1) CrudEntityTest::testDelete
Symfony\Component\HttpKernel\Exception\HttpException: Channel with ID 93 not found
I can see from the message that the 404 was thrown, but I cannot test the response object as planned. I know that in theory I could assert for the exception itself, but that is not what I want to do, I want to get the response (as a browser would do as well) and test for the status code itself.
Anybody any ideas how to reach that or if there is a better way to test this?
Thanks,
Daniel
This is how it is being done in the tests of Silex itself (see here):
public function testErrorHandlerNotFoundNoDebug()
{
$app = new Application();
$app['debug'] = false;
$request = Request::create('/foo');
$response = $app->handle($request);
$this->assertContains('<title>Sorry, the page you are looking for could not be found.</title>', $response->getContent());
$this->assertEquals(404, $response->getStatusCode());
}

Categories