how to adding error handler in slim3 middleware - slim

I started a project with slim3 framework. In my project I wrote a route group called admin for administrators.
$app->group('/admin', function () use ($app) {
$app->add( new AdminMiddleWare() );
$app->get('/books/{id}', function ($request, $response, $args) {
...
});
});
any of administrators should send a GET token for validation .
I want to create a middleware for checking admins tokens and if the token not set or is invalid display 403 error.
the Middleware class :
class AdminMiddleWare
{
/**
* Example middleware invokable class
*
* #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($request, $response, $next)
{
???
}
}
can you help me?

First of all there's a small improvement that you can do to the way you're adding the middleware.
$app->group('/admin', function () use ($app) {
$app->get('/books/{id}', function ($request, $response, $args) {
...
});
})->add( new AdminMiddleWare() );
Attach the middleware to the group not to the entire app.
As for your question, you'll have the query params available in the request object.
i.e. for an URL like example.com/admin/books/12?token=sf342ad you will have $params['token'] == 'sf342ad'
public function __invoke($request, $response, $next)
{
$params = $request->getQueryParams();
}
It might be easier to add the token as part of the route as you can generate the URL using reverse routing:
$app->group('/admin/{token}', function () use ($app) {
$app->get('/books/{id}', function ($request, $response, $args) {
...
})->setName('admin-book');
});
By doing it like this you'll have a token key in the $args array and it will match URLs like example.com/admin/sf342ad/books/1
And you can later build the route without hardcoding much:
$app->getContainer()->get('router')->pathFor('admin-book', ['token' =>'your token', 'id' => 'book id'])

Related

Slim framework 3: how to get parameters from a redirect from a post route inside a get route

This may seem a simple example but I am stuck. I am watching a tutorial where we see a contact form that has its own get route to render a view that contains the already said contact form.
$app->get('/contact',function (Request $request, Response $response){
return $this->view->render($response,'contact.twig');
})->setName('contact');
Then we have a post that gets the posted data through that form (notice I am passing the data that has been collected from the form).
$app->post('/contact',function ($request,$response){
$data = $request->getParams();
//var_dump($data);
return $response->withRedirect($this->router->pathFor('contact.confirmed', ['data' => $data]));//https://github.com/slimphp/Slim/issues/1933
})->setName('contact');
Finally we have another get route to render a confirmation view that lets the user know that the information has been submitted successfully.
$app->get('/contact/confirmed',function(Request $request, Response $response, $data){
echo 'params are';
var_dump($data);//They never show! :(
return $this->view->render($response,'contact_confirm.twig',[
'data' => $request->getParams(),
]);//https://github.com/slimphp/Slim/issues/1579
})->setName('contact.confirmed');
In that confirmation view, I want to retrieve the data submitted in the post route just to call the user by their name but I get an error saying that $data is empty.
I have been struggling on how to retrieve the user's name.
As a workaround, I have solved it by rendering the view right from the post route ...
$app->post('/contact',function ($request,$response){
$data = $request->getParams();
//var_dump($data);
return $response->withRedirect($this->router->pathFor('contact.confirmed', ['data' => $data]));//https://github.com/slimphp/Slim/issues/1933
})->setName('contact');
but then I wonder why bothering to use withRedirect() function?
My question is, how do I pass data or parameters from a post route to a get route wher you use a withRedirect() function? And in the get route, how do you retrieve those parameters? So I can pass them to its corresponding view.
Solved
Thanks to jmattheis this is how I solved it:
I just happened to learn how to use controllers in slim framework 3, so the ContactController.php looks like:
<?php
namespace App\Controllers\contact;
use App\Controllers\Controller;
class ContactController extends Controller
{
public function show($request,$response)
{
return $this->c->view->render($response,'contact.twig');
}
public function store($request,$response)
{
echo 'storing comments in db ...';
$data = $request->getParams();
//var_dump($data);
//echo 'Name is: '.$data['name'];
$this->c->flash->addMessage('data', $data);
return $response->withRedirect($this->c->router->pathFor('contact.success'));
}
public function success($request,$response,$data)
{
//echo 'Data to be sent in the redirect: ';
$data = $this->c->flash->getFirstMessage('data');
//var_dump($data);
//die();
return $this->c->view->render($response,'contact_success.twig',compact('data'));
}
}
And I added the following code
session_start();
ini_set('date.timezone', 'America/Mexico_City');
right before
$app = new App([
'settings' => [
'displayErrorDetails' => true
]
]);
And that did the trick.
You can't transfer data over a redirect. The second parameter from the pathFor method on the router is the array for the named paramters. That would be f.ex. id in the route /user/{id} then you'd had to put ['id' => 1] in it.
If you $data has a simple structur, then you could put them in the 3rd parameter which are the query params and later read them out with $request->getQueryParams()
$this->router->pathFor('contact.confirmed', [], $data);
Alternative, you could put the data in a session variable, slimphp/Slim-Flash is a library who that.
Example:
$app->post('/contact',function ($request,$response){
$data = $request->getParams();
$this->flash->addMessage('data', $data);
return $response->withRedirect($this->router->pathFor('contact.confirmed'));
})->setName('contact');
$app->get('/contact/confirmed',function(Request $request, Response $response, $data){
$data = $this->flash->getFirstMessage('data');
// ...
});
Assuming that confirmation template is rendered only after form is being processed, there is no need to declare a separate GET route for such view.
So, keep the route that renders the form:
// This route renders the contact form.
$app->get('/contact', function ($request, $response) {
return $this->view->render($response, 'contact.twig');
})->setName('contact');
And render contact_confirm.twig only as result of form being posted:
// This route is contact form processor.
$app->post('/contact', function ($request, $response) {
// Get submitted data.
$formData = $request->getParams();
// Process form data, e.g. store it, sending it somewhere...
// Render confirmation template, passing user name as template parameter.
$templateParams = [
'userName' => $formData['userName'] ?? 'Name unknown';
];
return $this->view->render($response, 'contact_confirmed.twig', $templateParams);
});

Using OAuth2 and ZF3-MVC to protect REST API

I'm trying to get https://github.com/zfcampus/zf-oauth2 working with my ZF3-MVC Application (ok, one solution could be to wait Apigility update).
I have successfully implemented the oauth2-server-php (https://github.com/bshaffer/oauth2-server-php), its zf-oauth2 module support (https://github.com/zfcampus/zf-oauth2) and adapted zf-oauth2 client for ZF3 (https://github.com/API-Skeletons/zf-oauth2-client).
However, I'm totaly stuck now trying to protect my API y following zf-oauth2 module's recommandation:
You can protect your API using the following code (for instance, at the top of a controller):
if (!$this->server->verifyResourceRequest(OAuth2Request::createFromGlobals()))
{
// Not authorized return 401 error
$this->getResponse()->setStatusCode(401);
return;
}
where $this->server is an instance of OAuth2\Server (see the AuthController.php).
I've read this post (Using ZF2 Oauth2) but it's not compliant with ZF3. I guess there's a more efficient way rather than copying/pasting zf-oauth2 module's controller and factory to instantiate the server from scratch.
Would anyone have a clue on how to implement the instance of OAuth2\Server in my API controller?
I finally did it by my own. As I spent a significant amount time on this and saw that others where also looking for a solution, here is how I did it.
At first, I suggest you read https://docs.zendframework.com/tutorials/in-depth-guide/models-and-servicemanager/ if you're not familiar with Dependency Injection and Factories (this was my case).
module.config.php
// In module/YourModule/config/module.config.php:
namespace YourAppNamespace;
use Zend\ServiceManager\Factory\InvokableFactory;
return [
'controllers' => [
'factories' => [
Controller\YourController::class => Factory\YourControllerFactory::class,
],
],
'service_manager' => [ /** Your Service Manager Config **/ ]
'router' => [ /** Your Router Config */ ]
'view_manager' => [ /** Your ViewManager Config */ ],
];
YourControllerFactory.php
// In module/YourModule/src/Controller/YourControllerFactory.php:
namespace YourAppNamespace\Factory;
use YourAppNamespace\Controller\YourController;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
class YourControllerFactory implements FactoryInterface
{
/**
* #param ContainerInterface $container
* #param string $requestedName
* #param null|array $options
*
* #return YourController
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$controllerPluginManager = $container;
$serviceManager = $controllerPluginManager->get('ServiceManager');
// Requires zf-campus/zf-oauth2
$server = $serviceManager->get('ZF\OAuth2\Service\OAuth2Server');
$provider = $serviceManager->get('ZF\OAuth2\Provider\UserId');
return new YourController($server, $provider);
}
}
YourController.php
// In module/YourModule/src/Controller/YourController.php:
namespace YourAppNamespace\Controller;
use ZF\OAuth2\Controller\AuthController;
use OAuth2\Request as OAuth2Request;
use ZF\OAuth2\Provider\UserId\UserIdProviderInterface;
class YourController extends AuthController
{
public function __construct($serverFactory, UserIdProviderInterface $userIdProvider)
{
parent::__construct($serverFactory, $userIdProvider);
}
public function indexAction()
{
$server = call_user_func($this->serverFactory, "oauth");
if (!$server->verifyResourceRequest(OAuth2Request::createFromGlobals())) {
// Failure
$response = $server->getResponse();
return $this->getApiProblemResponse($response);
}
// Success
echo json_encode(array('success' => true, 'message' => 'It works!'));
}
}
Hope it helps!

How to get the url for admin page (including the key) in magento 2x custom module controller

I need the page url in Magento 2x including key in my custom module controller.
here something similar but this is for magento 1x. i need for magento 2x.
for magento 1x : Mage::helper('adminhtml')->getUrl('adminhtml/framexport/index') but i need similar for magento 2x.
The right way is, inject the UrlInterface in you model block or whatever class constructor
Then call the getUrl() function
class SomeClass extends \Some\Other\Class
{
protected $_backendUrl;
public function __construct(
...........
...........
\Magento\Backend\Model\UrlInterface $backendUrl,
...........
) {
$this->_backendUrl = $backendUrl;
}
public function someFunction()
{
$params = array('some'=>'url_parameters');
$url = $this->_backendUrl->getUrl("the/url/path", $params);
}
}
You can easily get Admin url By calling
$this->getUrl('adminhtml/module/action');
Please not that "Context" type of object is loaded in the $this object
You can get admin url as follows:
public function __construct(\Magento\Backend\Helper\Data $HelperBackend
) {
$this->HelperBackend = $HelperBackend;
}
/**
*
* #param \Magento\Framework\Event\Observer $observer
* #return void
*/
public function getAdminUrl()
{
echo $this->HelperBackend->getHomePageUrl();
}
Somehow adminhtml/module creates an extra admin slug which does not work.
My solution is:
// $this->urlBuilder is defined in __constructor() \Magento\Framework\UrlInterface $urlBuilder
$query = [
'method' => 'confirm',
'id' => $order->getEntityId()
];
$url = $this->urlBuilder->getUrl('module', $query);
It works well for secure URLs (with keys) and non-default /admin URLs, such as /backend.

Laravel 4 list and detail route

I want to achieve the following:
devices > Controller#devices
devices/{id} > Controller#devices
Is this possible with Laravel ? I'm trying to map a domotic box with an android application ImperiHome, and they expect me to have the same route for devices list and for any device action.
So far I've tried this:
Route::get('devices/{deviceId}/action/{actionName}/{actionParam?}', 'DomoticzController#devices');
Route::get('devices', 'DomoticzController#devices');
But I cannot retrieve the argument when I call the devices/id url
Ok, so to solve the php strict standard error I just splitted the routes to two methods as follows:
routes.php
Route::get('devices/{deviceId}/action/{actionName}/{actionParam?}', 'DomoticzController#device');
Route::get('devices', 'DomoticzController#devices');
Route::get('rooms', 'DomoticzController#rooms');
//Route::get('action_ret', 'DomoticzController#action_ret');
Route::get('system', 'DomoticzController#system');
Route::get('/', 'DomoticzController#system');
DomoticzController.php
/**
* Call for an action on the device identified by $deviceId.
* #return string Json formated action status.
*/
public function device($deviceId, $actionName, $actionParam = null)
{
$client = $this->getClient();
$request = $client->getClient()->createRequest('GET', get_url("json.htm?type=command&param={$actionName}&idx={$deviceId}}&switchcmd=$actionParam"));
$response = $request->send();
$input = $response->json();
// convert to app format
$output = array('success' => ('OK' === $input['status'] ? true : false), 'errormsg' => ('ERR' === $input['status'] ? 'An error occured' : ''));
return Response::json($output);
}
/**
* Retrieve the list of the available devices.
* #return string Json formatted devices list.
*/
public function devices()
{
$client = $this->getClient();
$request = $client->getClient()->createRequest('GET', get_url('json.htm?type=devices&used=true'));
$response = $request->send();
$input = $response->json();
// convert to app format
$output = new stdClass();
$output->devices = array();
foreach ($input['result'] as $device) {
$output->devices[] = array (
'id' => $device['idx'],
'name' => $device['Name'],
'type' => 'DevSwitch',
'room' => null,
'params' => array(),
);
}
return Response::json($output);
}
maybe there is a better way to solve this, I would be glad to hear it.
If you let both routes use the same controller action, you need to make the parameters optional in the controller I think.
Try this public function device($deviceId = null, $actionName = null, $actionParam = null) and see if you still get the PHP strict error.
You can not have a route without parameters be redirected to a controller action that expects parameters. You can, on the other hand, make a route with parameters be redirected to a controller action with optional parameters (this does not mean that your route parameters need to be optional).

Zend Framework - Router - Creating Aliases

I'm building a Zend Framework 1.11.11 application and would like to make the routes and content database driven.
I've written a FrontController Plugin that retrieves the 'paths' from the database and creates an entry in the Router for each one, with the associated controller and action.
However, I'd like to be able to use 'aliases' - a URL that behaves like a normal URL, but is an alias.
For example, if I create the following:
// Create the Zend Route
$entry = new Zend_Controller_Router_Route_Static(
$route->getUrl(), // The string/url to match
array('controller' => $route->getControllers()->getName(),
'action' => $route->getActions()->getName())
);
// Add the route to the router
$router->addRoute($route->getUrl(), $entry);
Then a route for /about/ for example can goto the staticController, indexAction.
However, what's the best way for me to create an alias of this route? So if I went to /abt/ it would render the same Controller and Action?
To me it doesn't make sense to recreate the same route as I'll be using the route as the page 'identifier' to then load content from the database for the page...
you can extend static router:
class My_Route_ArrayStatic extends Zend_Controller_Router_Route_Static
{
protected $_routes = array();
/**
* Prepares the array of routes for mapping
* first route in array will become primary, all others
* aliases
*
* #param array $routes array of routes
* #param array $defaults
*/
public function __construct(array $routes, $defaults = array())
{
$this->_routes = $routes;
$route = reset($routes);
parent::__construct($route, $defaults);
}
/**
* Matches a user submitted path with a previously specified array of routes
*
* #param string $path
* #param boolean $partial
* #return array|false
*/
public function match($path, $partial = false)
{
$return = false;
foreach ($this->_routes as $route) {
$this->setRoute($route);
$success = parent::match($path, $partial);
if (false !== $success) {
$return = $success;
break;
}
}
$this->setRoute(reset($this->_routes));
return $return;
}
public function setRoute($route)
{
$this->_route = trim($route, '/');
}
}
and add new router this way:
$r = My_Route_ArrayStatic(array('about', 'abt'), $defaults);