a structural aid for creating express routes.
This code sets up an app with 3 handlers, 4 routes, and some middleware which
applies to different handler groups.
### Middleware Groups
Controller introduces the idea of middleware groups. This allows to you specify
named groups, and apply to middleware to every handler that is labelled with
this group. For example, you might have bunch of handlers that require you to
be logged in, and some middleware which checks authentication. You could add
all of the handlers to the 'require-login' group, and then add your auth
middleware to the 'reguire-login' group. This will now apply that middleware to
every handler in the group.
In action:
var stuff = controller();
stuff.define('sensitive-thing', ['require-login'], function(req,res){});
stuff.define('secret-thing', ['require-login'], function(req,res){});
stuff.middleware('require-login', function(req, res, next) {
if (isAuthenticated(req)) {
next();
} else {
res.send(403);
}
})
Special Groups
There are some special groups by default. The first one is all
, which applies
middleware to every action on this controller. Apart from that, every action
name is also a middleware group, so you can add middleware to individual actions
__Middleware Ordering__
Middlewares are called in the following order:
'all'
grouped middleware is executed first. (in the order they were added)- all other groups are then executed in the order they were added to the route
with
route
or direct
. within the group, middlewares are executed in the
order they were added. - route specific middleware (including middleware added inline on a
define
call) is then executed in the order it was added.
For example:
app.define('action', ['thing', MIDDLEWARE_1], routestr('str'));
app.use('thing', MIDDLEWARE_2);
app.use('thing', MIDDLEWARE_3);
app.use(MIDDLEWARE_4);
app.use(MIDDLEWARE_5);
app.use('action', MIDDLEWARE_6);
app.use('action', MIDDLEWARE_7);
app.route('get', '/action', 'action');
The call order of middleware in this example would be:
1. MIDDLEWARE_4 (global middleware)
2. MIDDLEWARE_5 (global middleware)
3. MIDDLEWARE_2 (group middleware)
4. MIDDLEWARE_3 (group middleware)
5. MIDDLEWARE_1 (route-specific middleware)
6. MIDDLEWARE_6 (route-specific middleware)
7. MIDDLEWARE_7 (route-specific middleware)
Special ordering conditions
- Route names can be group names too. In the previous example, if I was to specify
'action'
as the group for another route, the middleware added for 'action'
—
both inline and procedurally—will be called with group precedence, not route-
specific precedence. Route-specific precedence only applies to the middleware
added specifically for the current route.
### middleware(group\*, middleware\*)
Define some middleware(s) for a group(s). More than one middleware can be
passed, as well as more than one group. If you were to pass two groups and two
middlewares, each middleware would be added to both groups.
group
has some special values. 'all'
indicates that the middleware should
apply to every route on this controller. If you pass the name of an action as
the group, the middleware will apply to that action only.
Paramaters
group
- defaults to 'all'
middleware
- middleware to add to group
.
Example
users.middleware('auth', function checkAuthd(req, res, next) {
});
users.middleware(function(res, req, next) {});
users.middleware('getUser', function(req, res, next) {});
### Mounting controllers on controllers & middleware inheritance
You can mount a controller on another controller like so:
var appController = Controller();
var usersController = Controller();
appController.use('/users', usersController);
The path (in this case, '/users'
) is optional, but it usually makes senses.
Mounting controllers on each other in this way will cause middleware inheritance.
In our above example, this means that usersController
will inherit groups and
middlewares from appController
. If I set a global middleware on appController
,
usersController
will get it too. This means that, for example, if I have a
group 'auth'
on appController
, I can use it as normal on usersController
:
usersController.define('editUser', ['auth'], function(req, res) { ... });
appController.middleware('auth', function(req, res, next) { ... });
Middleware ordering when using inheritance
The run order of middleware is slightly different when utilising inheritance.
Normally, the global middleware runs first, then the middleware in the order
specified on define
. When utilising inheritance, this is still true, but within
a group, the lowest level of inheritance will run first.
The easiest way to demonstrate this is to show an example. Lets say we have 3
controllers inheriting from each other, such as this:
usersController.use('/cats', catController);
appController.use('/users', usersController);
Now, lets say that each of these 3 controllers have one global middleware:
appController.middleware(function(req, res, next) { console.log('app'); next() });
usersController.middleware(function(req, res, next) { console.log('users'); next() });
catController.middleware(function(req, res, next) { console.log('meow'); next() });
Lets also say that each of these 3 controllers have one middleware in a group
called 'auth'
.
appController.middleware('auth', function(req, res, next) { console.log('app (auth)'); next() });
usersController.middleware('auth', function(req, res, next) { console.log('users (auth)'); next() });
catController.middleware('auth', function(req, res, next) { console.log('meow (auth)'); next() });
And we have a route which consumes these middlewares:
catController.direct('get', '/meow', ['auth'], function(req, res) {
res.end('MEOW');
});
When we send a request to /users/cats/meow
, the output would be as follows:
app
users
meow
app (auth)
users (auth)
meow (auth)
So the middleware group order was:
appController [global]
usersController [global]
meowController [global]
appController [auth]
usersController [auth]
meowController [auth]
The ordering in which lower levels of middleware are called will not change,
regardless of the order they are added in.