I have just built a restful API with Slim Framework. For error conditions I simply respond with appropriate error codes for each error case and called with $app->halt, for example:
$app->halt(403, "Unauthorized");
But when I curled my API with -v and when I viewed headers in Firefox with HTTPFox I am always seeing error code 500. Anyone else notice this? Is there something I'm missing?
I ran into this same issue myself recently because I had forgotten to instantiate the $app variable within my function.
If you are not explicitly stating for your function to use($app), try adding the following line before $app-halt(403, 'Unauthorized') in order to see the desired error code:
$app = Slim::getInstance();
It is not allowed to call halt() method outside of the route callback.
You should use like this;
$app->get('/method/', function () {
//logical controls
//do something
//or
$app->halt();
});
There is a difference between halt() and setStatus().
With halt(), you will stop the current script execution and render a response according to the HTTP status code and message you choose to send. You can do it anywhere in your app with this code :
$app = \Slim\Slim::getInstance(); //if you don't have access to $app
$statusCode = 403;
$body = 'Unauthorized';
$app->halt($statusCode, $body);
//App will stop immediately
With setStatus() or $this->response->status(); you will only change the HTTP status code you are sending but your app will continue to execute like normally and won't stop. Its only changing the header that Slim will send to your client at then end of the route execution.
$app = \Slim\Slim::getInstance(); //if you don't have access to $app
$statusCode = 403;
$app->response->setStatus(400);
//App will continue normally
Related
I'm using Laravel 5.3 and I have this line under schedule() function in App\Console\Kernel.php.
$schedule->call('App\Http\Controllers\Controller#fetchXmlRpcResult')->everyMinute();
The function is sort of tedious but the jist of it: get the timestamp of the last record found in the database, create a new XML-RPC request asking for new records with start_date of the 'last found timestamp in the DB', decode the XML-RPC result and insert into the DB.
public static function fetchXmlRpcResult($user, $password, $account_id, $date)
{
$client = new Client('https://example.com/xmlapi/');
$client->setSSLVerifyPeer(false);
$client->setSSLVerifyHost(2);
$client->setCredentials($user, $password, CURLAUTH_DIGEST);
$parameters = [removed]
$request = new Request('getAccountData', $parameters);
$response = $client->send($request);
// removing the 401 HTTP Unauthorized header
$xml = (substr($response->raw_data, strpos($response->raw_data, "\r\n\r\n")+4));
// removing the OK HTTP header
$xml2 = (substr($xml, strpos($xml, "\r\n\r\n")+4));
$accountCDRs = xmlrpc_decode($xml2);
return $accountInfo;
}
When I run php artisan schedule:run in the console, I'm prompted with this error:
Running scheduled command: App\Http\Controllers\Controller#fetchXmlRpcResult
XML-RPC: PhpXmlRpc\Helper\Http::parseResponseHeaders: HTTP error, got response: HTTP/1.1 401 Unauthorized
[Symfony\Component\Debug\Exception\FatalThrowableError]
Call to undefined function App\Helpers\xmlrpc_decode()
The controller uses the file in App\Helpers\XmlHandler.php and in that file I use the following classes:
use PhpXmlRpc\Value;
use PhpXmlRpc\Request;
use PhpXmlRpc\Client;
Could the HTTP response be throwing it out? I tried executing the same function via the browser (aka putting the function in the route.php under http://example.com/update) and it worked perfectly fine.
You should try upgrading to the very latest version of the phpxmlrpc library.
It has fixed a bug that prevented it to be correctly used for making calls that use basic/digest auth using curl.
You could then remove the lines where you try to do manually the removal of the http headers from the response and the extra parsing of the response, and just use $response->value()
I'm using Silex's internal forwarding to map public URLs to internal URLs i.e. my.domain.com/something actually serves my.domain.com/something_else using
$subRequest = Request::create(
$redirect,
$method,
[], // params
$request->cookies->all(),
$request->files->all(),
$request->server->all()
);
if ($request->getSession())
{
$subRequest->setSession($request->getSession());
}
return $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST, true);
However, in Chrome's inspection tool, this appears as a 301 to the resulting page, which then serves the result. Is this "by design" as it represents an interesting security problem? Are there ways around this limitation?
While I can't post code for the something_else route controller, the gist is
// controller provider
$controller_factory->match('/something_else/{param}', function(...) {
include 'path/to/some/file';
});
and
// some/file - prepares a file to be downloaded
...
return new BinaryFileResponse();
there are no RedirectResponses in that file
Edit I over simplified in the above example. In reality, /something is a random string (i.e. /abcdefghijklmnopqrstuvwxyz which maps to one of many internal routes (-> /something_else, -> /something_else_2, -> etc).
No, I don't think so. I believe it is your something_else controller doesn't like the sub-request and returns redirect response, which your something controller unconditionally returns to the browser.
It can be anything. Silex is a micro-framework, which means things can be implemented in hundreds of different ways, and it is hardly possible to advise anything without seeing the actual code. It is the flip side of flexibility it brings. It may be RedirectableUrlMatcher, or anything in your controller, router, included files, error handler or middleware, which results with redirect response.
Consider this even more oversimplified single-script app example:
<?php
// web/index.php
require_once __DIR__.'/../vendor/autoload.php';
$app = new Silex\Application();
$app->get(
'/the-only-functional',
function() use ($app) {
return new \Symfony\Component\HttpFoundation\Response(
$app['request']->get('q')
);
}
);
$app->get(
'/{whatever}',
function($whatever) use ($app) {
$subRequest = \Symfony\Component\HttpFoundation\Request::create(
'/the-only-functional',
'GET',
['q'=>$whatever]
);
$response = $app->handle($subRequest);
if (200 != $response->getStatusCode()) {
throw new \Exception(
"Aha, that's where the problem lies"
. $response->getStatusCode() . ":"
. $response->getContent()
);
}
return $response;
}
)->value('whatever', 'nothing');
$app->run();
with http server running as:
php -S localhost:8081 -d "date.timezone=UTC" -t web web/index.php
you can try different permutations:
curl -v http://localhost:8081/
curl -v http://localhost:8081/blah-blah
curl -v http://localhost:8081/the-only-functional?q=direct
curl -v http://localhost:8081/?q=this+example+does+not+forward+query+string
and you always get 200 response.
Unfortunately, without sharing the code, you are on your own debugging your app. The only sensible advice is to analyse the sub-response before returning it, and may be log a backtrace to localise the problem.
The solution to this turned out to be a single character fix: /.
Given the following definition:
$controller_factory->match('/something_else/{param}/', function($app, $param) { ... });
$controller_factory->match('/something', function($app) {
// do the redirect to /something_else/{param}
$redirect = '/something_else/hello';
...
});
Can you spot it? It turns out that Symfony was intelligently forwarding routes without trailing slashes that matched routes with trailing slashes to the route with the slash. My issue was that I was "correctly" resolving /something_else/something from my string of random characters (see my edit if you missed this), but it was INCORRECT in the sense that it should be resolving to /something_else/something/ (notice the trailing slash)
You can take a look at Sub Request :
http://silex.sensiolabs.org/doc/cookbook/sub_requests.html
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
$app->get('/something', function (Application $app, Request $request) {
$subRequest = Request::create('/something_else', ...);
$response = $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false);
return $response;
});
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;
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());
}
Have a question on Slim Framework php.
In my application, I would like to stop the application execution if a condition is mismatched.
There is a halt function, per Slim documentation. But that does not appear to be working.
the application continuous to execute even after calling Halt.
pseudo code:
if ( $valid ) {
// Do something
} else {
$app->halt(500, "not valid");
}
// Other code here.
$app->run();
I was expecting that, we we call Halt function, the "Other code" should not execute.
But it does not appear to be the case.
Any ideas?
Halt should work. As Josh mentioned, it does need to be called from within a route callback. For example, I use the following as a custom 404 (inside of index.php) in the event that a specified route is not found.
// not found (custom 404)
$app->notFound(function () use ($app) {
// build response
$response = array(
'type' => 'not_found',
'message' => 'The requested resource does not exist.'
);
// output response
$app->halt(404, json_encode($response));
});
Note the reference to use ($app) -- more on this can be found here: http://www.slimframework.com/documentation/stable#scope-resolution
Halt should only be invoked within the context of a route callback. I recommend you ask any further questions on the official Slim Framework support forum:
http://help.slimframework.com
Best regards,
Josh
You can always call exit manually to stop the script execution.
$app->halt(500, "not valid");
exit;
You can use return if you are in a callback function
return $app->halt(500,"message");
or
$app->stop();
after halt function.