Slim 3.0 Getting Route Params in Middleware

Created on 22 Sep 2015  路  15Comments  路  Source: slimphp/Slim

I am creating a controller class and trying to add a method on my controller to load a object based on its route ID. I've got that all written and ready to work EXCEPT I don't know how to grab the route parameters from the request. There's also no documented way for middleware to access those parameters.

$app->group('/users', function () {
    $controller = new MyController();
    $this->get('/', [$controller, 'index']);
    $this->get('/{id}', [$controller, 'show'])->add($controller);
});
class MyController
{
    // Fulfills invoke status for Middleware, see http://www.slimframework.com/docs/concepts/middleware.html#invokable-class-middleware-example
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
    {
        // how do I get $routeParams???
        $userId = $routeParams['id'];
        $user = $userMapper->findById($userId);
        if ($user) {
           $request = $request->withAttribute('user', $user);
           return $next($request, $response);
        } else {
            // setup $response to contain a 404 response, don't call $next
            return $response;
        }
    }

    public function show(ServerRequestInterface $request, ResponseInterface $response, array $args)
    {
        $user = $request->getAttribute('user');  // already loaded by middleware
        return $response->getBody()->write(json_encode($user)); // varies, but for example
    }
Slim 3

Most helpful comment

Add following line in configuration
$config['determineRouteBeforeAppMiddleware'] = true;

In middleware
$route = $request->getAttribute('route');
$arguments = $route->getArguments();
print_r($arguments);

All 15 comments

I think I've figured out a solution to my problem; however, I still would like to know how a middleware would access router parameters or what people think on that being poor middleware design

For now I'm just doing the __call() magic method on my controller and making my show action private. Something like:

$app->group('/users', function () {
    $controller = new MyController();
    $this->get('/', [$controller, 'index']);
    $this->get('/{id}', [$controller, 'show']);
});
class MyController
{
    public function __call($name, array $arguments)
    {
        list($request, $response, $args) = $arguments;
        $user = $userMapper->findById($args['id']);
        if ($user) {
            $request = $request->withAttribute('user', $user);
            return call_user_func([$this, $name], $request, $response, $args);
        } else {
            // $response now contains 404 response, don't call method
            return $response;
        }
    }

    private function show(ServerRequestInterface $request, ResponseInterface $response, array $args)
    {
        $user = $request->getAttribute('user');  // already loaded by middleware
        return $response->getBody()->write(json_encode($user)); // varies, but for example
    }
}

You'll find the route parameters in the third element of the 'routeInfo' attribute. We'd love a PR documenting this :)

$routeParams = $request->getAttribute('routeInfo')[2];

As a side note, this option does not work for application middleware

Agree with @bradynpoulsen that it doesn't work for middleware. Would also be able to access route arguments from within middleware

By default, the route hasn't been worked out when app middleware is running, which is why it's not working for you.

If you need this, then you can enable this setting:

$settings = [
    'settings' => [
        'determineRouteBeforeAppMiddleware' => true,
    ],
];
$app = new Slim\App($settings);

@akrabat - this does solve the problem. Thanks!

This works @kevinmlong:

$settings = [
    'settings' => [
        'determineRouteBeforeAppMiddleware' => true,
    ],
];
$app = new \Slim\App($settings);

$app->get("/{name}", function ($request, $response, $args) {
    return $response->write("Hello {$args['name']}");
});

$app->add(function ($request, $response, $next) {
    var_dump($request->getAttribute('routeInfo')[2]);

    return $next($request, $response);
});

Navigating to http://develop-testbed.slim3.dev/rob gives me:

/www/dev/slim3/develop-testbed/public/index-scratch.php:23:
array (size=1)
  'name' => string 'rob' (length=3)
Hello rob

@akrabat - I sent the other comment to quickly. I suppose I deleted it after you saw it. Thanks!

A related issue ....

In my middleware, I am calling

$next($request,$response);
-- doing stuff --

As it goes through the routing methods, it returns a response. In some cases it changes the status to a different code (e.g. 400) like below:

$newResponse = $this->response->withStatus(404)
                                     ->withHeader('Content-Type', 'application/json')
                                     ->write(...stuff...);
return $newResponse;

when the application returns to the middleware, the response object has a code of 200. When returned, it has the right body, but the wrong status. Thoughts?

@kevinmlong $this->response is probably stale. Where is that coming from?

It's in the route method

$app->get('path to route', function ($request, $response, $args) {
    do some stuff
    $newResponse = $this->response->withStatus(404)
                                     ->withHeader('Content-Type', 'application/json')
                                     ->write(...stuff...);
return $newResponse;
}

The code then returns to the middleware. The code response here is 200 instead of 404. If I return the response, I get the right body from the write(..stuff..) but the code is not 404.

@kevinmlong you need to use $response instead of $this->response. $this->response is a stale response object while $response is the current one from this request. You can even do exactly what @akrabat did in his example.

$app->get('path to route', function ($request, $response, $args) {
    return $response->withStatus(404)
                   ->withHeader('Content-Type', 'application/json')
                   ->write(...stuff...);
});

@silentworks - that doesn't work. it still returns to the middleware with a 200 code.

@silentworks - the response object is getting updated, since the body is being updated correctly.

@silentworks - I figured it out. Need to change the middleware to:

public function __invoke($request, $response, $next){
// do some stuff
$newResponse = $next($request,$response);
// do more stuff
return $newResponse;
}

Add following line in configuration
$config['determineRouteBeforeAppMiddleware'] = true;

In middleware
$route = $request->getAttribute('route');
$arguments = $route->getArguments();
print_r($arguments);

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Bilge picture Bilge  路  28Comments

derekjones picture derekjones  路  19Comments

l0gicgate picture l0gicgate  路  83Comments

konskript picture konskript  路  62Comments

feryardiant picture feryardiant  路  20Comments