Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slim 3.0 Getting Route Params in Middleware #1505

Closed
bradynpoulsen opened this issue Sep 21, 2015 · 15 comments
Closed

Slim 3.0 Getting Route Params in Middleware #1505

bradynpoulsen opened this issue Sep 21, 2015 · 15 comments
Labels

Comments

@bradynpoulsen
Copy link

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
    }
@bradynpoulsen
Copy link
Author

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
    }
}

@akrabat
Copy link
Member

akrabat commented Sep 22, 2015

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];

@bradynpoulsen
Copy link
Author

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

@kevinmlong
Copy link

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

@akrabat
Copy link
Member

akrabat commented Nov 18, 2015

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);

@kevinmlong
Copy link

@akrabat - this does solve the problem. Thanks!

@akrabat
Copy link
Member

akrabat commented Nov 18, 2015

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

@kevinmlong
Copy link

@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?

@silentworks
Copy link
Member

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

@kevinmlong
Copy link

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.

@silentworks
Copy link
Member

@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...);
});

@kevinmlong
Copy link

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

@kevinmlong
Copy link

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

@kevinmlong
Copy link

@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;
}

@HarshVarshney
Copy link

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants