How can I get route pattern inside middleware:
routes.php:
$app->get('/myroute/{id}', function($req, $res, $args) {
//DO STUFF HERE
})->add(new MyMiddle());
middle.php:
class MyMiddle {
public function __invoke($req, $res, $next) {
//DO STUFF
}
}
In routes.php I can get {id} with $args['id'], but how can I get it inside MyMiddle.php?
Thank you,
Cristian Molina
Enable the determineRouteBeforeAppMiddleware setting:
$config = ['settings' => [
'determineRouteBeforeAppMiddleware' => true,
'displayErrorDetails' => true,
]];
$app = new \Slim\App($config);
You can now access the Route object from the Request, using getAttribute() and, from the route, get at the arguments:
$app->add(function ($request, $response, $next) {
$id = $request->getAttribute('route')->getArgument('id');
return $next($request, $response);
});
I decided to included a Slim v2 example as that is what I was looking for when I came across this post. You can use $this->app->router()->getCurrentRoute()->getPattern() from the slim.before.dispatch callback hook to accomplish the same thing.
Related
I am trying to use a middleware in Laravel 7 to fake Http calls to a 3rd party API. So I can assign that middleware to any route which will make calls to that 3rd party API. So whenever that route is called, it will call to the faked API.
Purpose of this is, when I want to fake the API, I just only have to assign the middleware to the route. When I don't want to fake the API, I will just remove the middleware from the route.
The middleware looks like below.
namespace App\Http\Middleware;
class MockApiEndpoints
{
public function handle($request, Closure $next)
{
// mock api/endpoint
$vacancies = $this->vacancyRepository->all();
$url = 'api/endpoint';
$sessionId = Str::uuid()->toString();
$response = [
'result' => 'OK',
'content' => [
'suggestedVacancies' => array_map(function ($uid) {
return [
'id' => $uid,
'relevance' => bcdiv(rand(9, 99), 100, 2)
];
}, $vacancies->pluck('uid')->all()),
'sessionId' => $sessionId
]
];
$this->mockHttpRequest($url, $response);
return $next($request);
}
protected function mockHttpRequest(string $uri, $response, int $status = 200, array $headers = [])
{
$url = config('api.base_url') . '/' . $uri;
Http::fake([
$url => Http::response($response, $status, $headers)
]);
}
}
Even though I attach this this middleware to the route, route still makes calls to the original API. So the Htpp::fake doesn't work in the middleware it seems. Htpp::fake does work if I use it inside the controller.
Middleware is attached to the route as below. (Middleware is properly registered in the $routeMiddleware array in app/Http/Kernal.php)
namespace App\Providers;
class RouteServiceProvider extends ServiceProvider
{
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware(['MockApiEndpoints'])
->namespace($this->namespace . '\Api')
->group(base_path('routes/api.php'));
}
}
I got my work done by using a workaround. But I want to know why does Http::fake doesn't work in middleware. Thank you for your answers in advance.
Instead of returning $next($request) you should return a response(...) in your middleware.
Perhaps just forward the response from the fake call.
return response($responseFrom3rdPartyApiCall, 200);
I'm building a rest api with slim 3 but I'm having some trouble understanding how to do the routing. I initially had the get method working correctly at api.com/v1/companies/get and api.com/v1/companies/get/id and post method at api.com/v1/companies/post, but I refactored so all the methods would be at api.com/v1/companies/id and after this refactoring I get a 405 error on post requests saying that only the get method exists.
So I did a little more researching; the amount of small, but breaking inconsistencies I've found in other slim 3 guides has been a bit annoying, but it looks like my solution is the map() function, only I have no idea how to use it, and even the official docs skip over the part I don't understand.
This is how the code looked after the refactor that broke it:
$app->group('/v1', function() use ($app) {
$app->group('/companies', function() use ($app) {
$app->get('/{code}', function($request, $response, $args) {...}
$app->get('', function($request, $response, $args) {...}
$app->post('', function($request, $response, $args) {...}
});
});
And my first attempts at using map():
$app->group('/v1', function() use ($app) {
$app->map(['GET', 'POST', 'PUT', 'DELETE'], '/companies/{code}', function($request, $response, $args) use ($app) {
//here's where I don't know what to do
if($request->isGet()) {
//What goes here? And I seem to be having problems accessing my parameters?
}
}
}
This code works for me:
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
require '../vendor/autoload.php';
$app = new \Slim\App;
$app->group('/v1', function() {
$this->map(['GET', 'POST', 'PUT', 'DELETE'], '/companies/{code}', function($request, $response, $args) {
if($request->isGet()) {
$response->getBody()->write("it's GET");
}
if($request->isPost()) {
$response->getBody()->write("it's POST");
}
if($request->isPut()) {
$response->getBody()->write("it's PUT");
}
if($request->isDelete()) {
$response->getBody()->write("it's DELETE");
}
return $response;
});
});
$app->run();
Please don't use $app inside group. In docs you can see that $this inside group point to instance of 'Slim\App' already. Also check your .htaccess file if it's configured as described in Slim3 documentation.
I need to redirect from a controller to another.
$app->get('/test', function(Request $request) use ($app) {
return $app->redirect($app["url_generator"]->generate("success", [
"myArg" => $myArg
]));
});
$app->get('/test/success', function(Request $request, $myArg) use ($app) {
return $app['twig']->render('confirmation.twig', [
'myArg' => $myArg,
]);
})->bind('success');
But it seems to doesn't be the good way to do it... I just want to redirect to the route "/test/success" and passing the $myArg variable to my confirmation.twig template.
Thanks for help.
By putting $myArg in the arguments of your function, Silex expects it to be a parameter from your URL, however your route definition isn't dynamic so it can't work.
If you want to get the parameter from the arguments of your function you have to modify your route definition to something like /test/success/{myArg}.
I'm trying to implement json-schema validator from justinrainbow as middleware in Slim 3.
I can't figure out how to get the clients input from GET/POST requests in middleware.
tried like this:
$mw = function ($request, $response, $next) {
$data = $request->getParsedBody();
print_r($data); // prints nothing
$id = $request->getAttribute('loan_id');
print_r($id); // prints nothing
// here I need to validate the user input from GET/POST requests with json-schema library and send the result to controller
$response = $next($request, $response);
return $response;
};
$app->get('/loan/{loan_id}', function (Request $request, Response $response) use ($app, $model) {
$loanId = $request->getAttribute('loan_id'); // here it works
$data = $model->getLoan($loanId);
$newResponse = $response->withJson($data, 201);
return $newResponse;
})->add($mw);
There are 2 possible ways of how I need it. what i'm doing wrong ?
validate it in middleware and send some array/json response to the controller, which i will then get as I understood with $data = $request->getParsedBody();
validate it in middleware but final check will be in controller like this:
$app->get('/loan/{loan_id}', function (Request $request, Response $response) use ($app, $model) {
if($validator->isValid()){
//
}
$loanId = $request->getAttribute('loan_id'); // here it works
$data = $model->getLoan($loanId);
$newResponse = $response->withJson($data, 201);
return $newResponse;
})->add($mw);
Best option for me it do something like here
but I don't understand what should i return in container, and how to pass get/post input to container
Your code in the first point seems alright, you only try to access route parameter from within middleware. At that point the route is not yet resolved and therefore parameters are not parsed from the URL.
This is a known use case and is described in Slim's documentation. Add the following setting to your app configuration to get your code working:
$app = new App([
'settings' => [
// Only set this if you need access to route within middleware
'determineRouteBeforeAppMiddleware' => true
]
]);
In order to understand how middleware works and how to manipulate response object, I suggest you read the User Guide - it's not that long and explains it really well.
I'm learning to use the slim framework (v3) and I'm stuck with trying to make a redirect with a flash message. I have a home URL and a /flash URI that should set a flash message and redirect to home.
My bootstrap code is:
use Slim\App;
use Slim\Container;
use Slim\Flash;
$container = new Container(['settings' => ['displayErrorDetails' => true]]);
$container['view'] = function ($container) {
$view = new \Slim\Views\Twig([
INC_ROOT. '/app/views',
INC_ROOT. '/app/views/templates',
INC_ROOT. '/app/views/templates/partials',], [
'debug' => true
]);
$view->addExtension(new \Slim\Views\TwigExtension(
$container['router'],
$container['request']->getUri()
));
$view->getEnvironment()->addGlobal('flash', $container['flash']);
return $view;
};
$container['flash'] = function () {
return new Flash\Messages();
};
$app = new App($container);
My routing file is:
$app->get('/', function($request, $response, $args){
return $this->view->render($response, 'home.twig');
})->setName('home');
$app->get('/flash', function ($req, $res, $args) {
// Set flash message for next request
$this->flash->addMessage('global', 'This is a message');
// Redirect
return $res->withStatus(301)->withHeader("Location", $this->router->pathFor('home'));
});
And I'm trying to use the message in twig as:
{{ flash.getMessage('global')[0] }}
The redirect is happening, but the flash message is not been passed to the new location. What I'm I doing wrong?
A better way would be to use a middleware for the flash messages, then you should be able to access the flash messages in your View, do this..
//Add a middleware to your app
$app->add(function ($request, $response, $next) {
$this->view->offsetSet("flash", $this->flash);
return $next($request, $response);
});
$app->get('/flash', function ($req, $res, $args) {
// Set flash message for next request
$this->flash->addMessage('global', 'This is a message');
$this->view->render($res, 'home.twig');
});
And then call it from View like this,
{{ flash.getMessage('global')[0] }}
I think that you can understand by reading Slim-Flash page.
routing file
$app->get('/', function($request, $response, $args){
$messages = $this->flash->getMessages()['global'][0];
return $this->view->render($response, 'home.twig', ['messages' => $messages]);
})->setName('home');
$app->get('/flash', function ($req, $res, $args) {
// Set flash message for next request
$this->flash->addMessage('global', 'This is a message');
// Redirect
return $res->withStatus(301)->withHeader("Location", $this->router->pathFor('home'));
});
twig file
{{ messages }}