Shouldn't route middleware be called before app middleware?


#1

I would like to have a middleware executed only on certain routes, but I would like it to execute before my application middleware.

When reading the doucmentation on middleware it says:

The last middleware layer added is the first to be executed.

Shouldn’t this mean that route middleware be executed first since it was added last?

My usecase is I want a middleware to validate an auth token api token on all routes by default unless flagged as publicly accessible, I would use a route middleware to set an attribute when an endpoint is publicly accessible. But since I can’t force route middleware to execute first not sure how I can do this.


#2

Only if you add it last, I believe. Take for instance the Slim Skeleton’s index.php, but with the following tweaks.

require __DIR__ . '/../vendor/autoload.php';

// Instantiate the app
$settings = require __DIR__ . '/../src/settings.php';
$app = new \Slim\App($settings);

// Set up dependencies
require __DIR__ . '/../src/dependencies.php';

// Register middleware (added before route middlewares are added)
$app->add(new \App\Middleware\SomeAwesomeMiddleware);

// Register routes
require __DIR__ . '/../src/routes.php';

// Register another middleware (added after route middlewares are added)
$app->add(new \App\Middleware\AnotherAwesomeMiddleware);

// Run app
$app->run();

You could also do it via includes like the Skeleton shows. So here you can add some middleware before you add your route middleware and register more middleware after your route middleware. (Which then stacks/orders backwards on the way in towards the app.)


#3

Hi thanks for the reply… yes if I add app level middleware after the routes the order is preserved , but If I add route level middleware it gets called after the app level ones.

Meybe a bit of code would help to explain:

$app = new \Slim\App

$app->add(function($req, $res, $n) {
   $res->getBody()->write("mid1\n");
   return $n($req, $res);
});

$app->get('/', function ($req, $res) {
   $res->getBody()->write("body\n");
   return $res;
})->add(function($req, $res, $n) {
   $res->getBody()->write("mid2\n");
   return $n($req, $res);
});

$app->add(function($req, $res, $n) {
   $res->getBody()->write("mid3\n");
   return $n($req, $res);
});

$app->run();

I get:

mid3
mid1
mid2
body

But would expect to get:

mid3
mid2
mid1
body

Where mid2 is the text injected with the route level middleware.


#4

Yes, I see that now. What about adding two middlewares to the route, the first one you add would check for the public attribute and if so pass along to the $next middleware. If there is no public attribute then it would validate the token.

<?php

require __DIR__ . '/../vendor/autoload.php';

$app = new \Slim\App;

$app->add(function ($req, $res, $next) {
    $res->getBody()->write("second\n");
    $res = $next($req, $res);
    $res->getBody()->write("eighth\n");
    return $res;
});

$app->get('/', function ($req, $res) {
    $res->getBody()->write("fifth->app/body\n");
    return $res;
})->add(function ($req, $res, $next) {
    $res->getBody()->write("fourth->checkAttributeHere\n");
    $res = $next($req, $res);
    $res->getBody()->write("sixth\n");
    return $res;
})->add(function ($req, $res, $next) {
    $res->getBody()->write("third->addAttributeHere\n");
    $res = $next($req, $res);
    $res->getBody()->write("seventh\n");
    return $res;
});

$app->add(function ($req, $res, $next) {
    $res->getBody()->write("first\n");
    $res = $next($req, $res);
    $res->getBody()->write("ninth\n");
    return $res;
});

$app->run();

outputs

first
second
third->addAttributeHere
fourth->checkAttributeHere
fifth->app/body
sixth
seventh
eighth
ninth

But I see where adding that (in the case above forth) middleware would be tricky if you want to add it to everything in a group at the end. :frowning: Maybe use determineRouteBeforeAppMiddleware and inspect the route to check and see if the route is public?


#5

Hi,

I ended up doing like you said just creating a list of public routes to the app milddleware rather than the route one. Since it’s for authorization I want to block most unauth requests rather than risking forgetting to add the middleware I chose to block all unless specified in the list.

Thanks for all you help.

Cheers,

  • Pat