After restructuring my silex1.2 application, I am now getting hit by:
exception 'RuntimeException' with message 'Accessed request service
outside of request scope. Try moving that call to a before handler or
controller.' in
../vendor/silex/silex/src/Silex/Application.php:150
I used to setup the appilcation's config this way:
$app = new Silex\Application();
$app->register(new ServiceControllerServiceProvider());
$app->register(new ConfigServiceProvider($configFile));
$fileFinder = new \Symfony\Component\Finder\Finder();
foreach ($fileFinder->in($configPath . 'country') as $file) {
/* #var SplFileInfo $file */
$app->register(new ConfigServiceProvider($file->getRealPath()));
}
I now wanted to replace the foreach loop by injecting a specific value which I get from the user's request. So I wanted to access $request->query->get('country'); yet I cannot since app['request'] is out of scope at that point.
I don't understand the error message, as in:
When and why are request objects out of scope in silex?
How am I supposed to move the call before handler and controllers when it is the very first thing I do on setting up my application?
Basically, I want to access request data early on on in order to get one value. How can I achieve that in order to bootstrap application accordingly?
You try to use Request before it is initialized, before $app->run().
You can manually initialize Request:
$app = new \Silex\Application();
$app['request'] = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
.....
$app->run($app['request']);
or make lazy loading in service providers:
$app['object1'] = $app->share(function ($app) {
return new Object1($app['request']->query->get('country'));
});
...
and somewhere in controller get these variable as $app['object1']
Related
Is there a way to call a routine before each route in Slim PHP? I have a RESTful API and I want to validate the login before calling the API methods. My code looks like:
$app = new Slim();
$app->get('user/:id', function($id) use($app){
$user = API::getUser($id);
if($user){
$app->response->status(200);
}else{
$app->response->status(404);
}
});
The API makes the request and process a JSON response. I want to attach a precondition to allow the request, something like a callback. The API has a method API::validate($token) that returns true or false, I want to catch this and return status code 401 if authentication fails. Some methods like API::login() and API::register() don't need this validation.
First i strongly advice you to read ALL the documentation. It isn't that big and you'll get a good introduction to the framework. http://docs.slimframework.com/
You don't need a routine but a hook that gets called before each routine. That's the way slim is structured.
A easy solution would be to keep the non protected pages in a array, and in that hook check if the request is protected or not.
$app->hook('slim.before.dispatch', function() use ($app) {
$publicRoutes = array('login', 'welcome');
if(!in_array($app->router()->getCurrentRoute(), $publicRoutes)
// Get the token
$result = API::validate($token);
if(!$result) {
$app->redirect('/login');
}
});
If you want to handle a more complex process with permissions levels, oauths, etc you'll rather use a Middleware. I like them because it's the right way to do this kind of tasks with Slim, and you can reuse them.
I am calling one function from onBootStrap() to authorize user, in that function I am using header information to verify the user.
If this is not correct, I want to stop execution here(onBootStrap()) without even calling the actual API and return some response to the user .
User should get some response because then only user can know what's the problem.
How I can return response from there?
Simply said, onBootstrap is not sufficient for this. Usually, you have two stages in your application. The first is bootstrapping, the second is running. During run you can authorize users and return responses, during bootstrap this is not possible.
The reason is simple, you might have another module overriding it's behaviour. If you stop bootstrapping after your module, you can stop the execution of these modules. It's better to move the logic to run. This run stage is defined with various listeners, of which the first is route. There isn't much going on after bootstrap and before route, so in terms of performance it's neglectable.
A code example:
use Zend\Mvc\MvcEvent;
use Zend\Json\Json;
class Module
{
public function onBootstrap($e)
{
$app = $e->getApplication();
$em = $app->getEventManager();
$em->attach(MvcEvent::EVENT_ROUTE, function($e) use ($app) {
// your auth logic here
if (!$auth) {
$response = $e->getResponse();
$response->setStatusCode(403);
$response->setContent(Json::encode(array(
'error' => 12345,
'message' => 'You are not authorized for this request',
));
return $response;
}
}, PHP_INT_MAX);
}
}
The listener is attached at an very early stage (PHP_INT_MAX) so the check happens as first in the complete route stage. You can also choose for quite a high number (like, 1000) so you can hook in this event before user authorization.
when i send a jsonp GET request with jQuery, it usually sends something like:
http://website.com/test?callback=jQuery20309569547907449305_1386221743664&id=9&limit=10&_=1386221743665
in Zend Framework i will handle this like:
$request = $this->getRequest();
$callback = $request->getParam('callback');
$id = $request->getParam('id');
$limit = $request->getParam('limit');
// set $response var to something
$this->getResponse()->setBody($callback . '(' . json_encode($response) . ');');
in Slim Framework i have:
$callback = isset($_GET['callback']) ? $_GET['callback'] : '';
$app->get(
'/test',
function () {
$resp = array('This is a TEST route');
}
);
$app->response->setBody($callback . '(' . json_encode($resp) . ');');
but the route returns 404
any ideas how can i have this working?
For Slim 3.x Just add the middleware in the response chain
$app->add(function ($request, $response, $next) { // jsonp
$callback = $_GET['callback'] ?? false;
if($callback) $response->getBody()->write($callback."(");
$response = $next($request, $response);
if($callback) $response->getBody()->write(")");
return $response;
});
There are several things wrong here. First, you should not be getting a 404, you should be getting an error complaining that $resp is not defined.
I think you are probably missing a .htaccess (or web.config if you are on IIS) that is routing all requests to your front controller file (where you define your Slim object and routes). To see if this is the problem, try http://website.com/index.php/test?callback=whatever, where index.php is the name of your front controller file.
This is the .htaccess that I use:
RewriteEngine On
#Slim PHP routing
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_URI} !^/index.php
RewriteRule ^ index.php [QSA,NC,L]
As for trying to achieve what you want to achieve, you need something like this:
$app = new Slim\Slim();
$app->get('/test', function () use($app) {
//Request processing begins here...
//Get callback from query string
$callback = $app->request()->get('callback');
//Check for null here...
//Set content type to javascript
header('Content-type: text/javascript');
//Generate our JSONP output
echo "$callback(" . json_encode(array('This is a test.')) . ");";
//Request processing ends here...
});
$app->run();
I'm not 100% familiar with Zend, but I think it uses a more traditional MVC implementation where you have a Controller class that you extend and implement actions as methods. Slim is much more basic than that, instead you define routes on your app objects and map these to closures, which are executed when their route is hit.
In my example above, I define a closure for the route '/test'. Closures in PHP have no access by default to other variables in their scope. In order to access a variable outside of the closure scope we must explicitly specific the variables we want via the "use" keyword. In the example, I "use" the $app object, so that we can use the app object inside our closure. This is the basis for the majority of the functionality Slim provides. The $app object is the IOC object, the core where everything lives and should be used to expose service objects, etc. In this case, we are using the request() method that returns us a wrapper around the request related superglobals ($_GET, $_POST, etc).
Once we have our callback parameter, we can validate, and then generate and send our JSONP. Slim does not abstract (as far as I know) send data back down the response, you should just use echo as in vanilla PHP. You should also set the header type to javascript since that is what we are sending. Hope this helps.
Have you worked with zend before? I'm not quite sure if you know how zend works. You don't have any get() functions with callbacks, but rather you've got an controller (in your case: test) and this controller has several actions.
an example for your text-controller with an example action could look something like this:
class TestController extends Zend_Controller_Action
{
public function init()
{
//you might want to use here a contextSwitch
}
public function fooAction()
{
//get params
$limit = $this->_getParam('limit', 0);
[...]
//do stuff here
[...]
$this->_helper->json($response);
}
}
Your calls to this action now might look like this:
http://website.com/test/foo/limit/10
Note that there is no need for ugly ?param=value in zend. simply append it to you URI with param/value
Note (important):
There are several ways in zend to output json, therefore $this->_helper->json($response) might not be the best solution for you. Using a contextSwitch inside your init() function might be better.
I came across this answer while attempting to support JSONP responses in Slim v3.
The answer by #cardeol didn't quite support my needs as some of my earlier middleware calls made use of the "$response->withJson($data, $code);"
This call destroys and recreates the body object. Flushing any "BEFORE" writes.
class JSONPResponseMiddleware {
/**
* Wrap response with callback query parameter
*
* #param \Psr\Http\Message\ServerRequestInterface $request PSR7 request
* #param \Psr\Http\Message\ResponseInterface $response PSR7 response
* #param callable $next Next middleware
*
* #return \Psr\Http\Message\ResponseInterface
*/
public function __invoke(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, $next){
if(!$callback = $request->getQueryParam("callback", false)){
return $next($request, $response);
}
$response = $next($request, $response);
$data = $response->getBody();
// upstream controllers use "withJSON" which purges the existing body object.
$body = new Body(fopen('php://temp', 'r+'));
$body->write("{$callback}(");
$body->write($data);
$body->write(")");
$response = $response->withBody($body)->withHeader('Content-type', 'application/javascript');
return $response;
}
}
Then installed with:
$app->add(new JSONPResponseMiddleware());
Im making a restful web service with Zend FW 2. How can I create a system, that checks ie. API key everytime REST is called? Checking the key in every controller in every function of course is not the way to go, so Im looking for something "global".
Thanks!
Assuming all your rest methods are in one controller you can listen to that controllers dispatch event, using a high priority so checks are done early...
Register the listener in your modules bootstrap, for example assuming you added an ApiController to the Application module
public function onBootstrap(EventInterface $e)
{
$app = $e->getApplication();
// get the shared events manager
$sem = $app->getEventManager()->getSharedManager();
// listen to dispatch event when triggered by the ApiController
$sem->attach('Application\Controller\ApiController', 'dispatch', function($e) {
// do your api key checks
// if checks fail get the response from the controller
$controller = $e->getTarget();
$response = $controller->getResponse();
$response->setStatusCode(401);
// return $response, short circuiting dispatch event
return $response;
}, 9000); // 9000 = high priority, do this early
}
Point of note, the event passed to your closure contains as its target an instance of your controller, so if you need to get services from the ServiceManager to do your api checks you can do so just like you would in the controller itself, ie...
$controller = $e->getTarget();
$sm = $controller->getServiceLocator();
I'm trying to figure out what is going on below:
public function serviceAction(){
$config = Zend_Registry::get('config');
$client = new Zend_Http_Client();
$client->setAuth($config['api']['username'],$config['api']['password']);
$service = new Krixton_JsonRpc_Client($config['api']['endpoint'], $client);
switch($this->_getParam('task'))
{
case 'test':
if(!this->getParam('newsletter_id')){
$this->_helper->json(array('sent'=>false,'error'=>'Newsletter ID is invalid, must be numeric'));
return;
}
$request = $service->call('newsletter.send', array($this->_getParam('newsletter_id'),false));
$this->_helper->json($request->result));
break;
}
}
What I'm trying to find out is how does
`Zend_Registry::get('config')`, $client->setAuth and $service->call`
works?
I understand _getParam('task') is a method to get get or post variables but not sure about the others. I had a look through some Zend documentations but if someone could help me out that would be appreciated!
Two things are happening there, the first one is Zend_Registry. get()allow you to get a value previously registered in the registry via Zend_Registry::set('key', $value). Usually, 'config' is your application configuration, which is the application.ini file.
basically, you would register config using this bootstrap method:
protected function _initConfig()
{
$config = new Zend_Config($this->getOptions());
Zend_Registry::set('config', $config);
return $config;
}
The second ones are nothing else than methods of Zend_Http_Client. setAuth()is used to set a basic HTTP authentification, and call()is an internal method of your object Krixton_JsonRpc_Client.
If you're trying to understand deep down how does these methods work, you should read the man first (especially Zend_registry and Zend_Http_Client pages) and then read carefully the source code.
Zend_Registry::get('config') ('config' is name of an array in this case) is recalling data that was saved to the registry, probably in the Bootstrap.php to make the information in the application.ini(configuration file) available everywhere.
The Bootstrap.php probably caontains something similar to:
protected function _initRegistry() {
//make application.ini configuration available in registry
$config = new Zend_Config($this->getOptions());
Zend_Registry::set('config', $config);
}
$client->setAuth is simply providing user credentials to Zend_Http_Client() HTTP LINK that were stored in the configuration file and accessed through the $config array.
$service->call I'm quite sure what this doing because I'm not familiar with the class (likely custom) being used. It looks like a request for a newsletter is being made based on 'id'.