Reputation: 53
I'm trying to wrap my head arround the middleware in ExpressJS 4.
If I understood correctly is that middleware are applied in the order of declaration and that you can "bind" them at different level.
Here I'm trying to bind a middleware at router level.
function middleware(req, res, next) {
console.log("middleware");
res.write("middleware");
next();
}
function handler(req, res) {
console.log("OK");
res.status(200).send('OK');
}
const router1 = express.Router();
const router2 = express.Router();
router1.get("/1", handler);
router2.get("/2", handler);
I would except the following to print OK
when calling /test/1
and middleware
on /test/2
.
app.use("/test/", router2.use(middleware), router1);
But the output seems to be inverted and is equivalent to:
app.use("/test/", router2, middleware, router1);
What I really want is that only the first router to use the middleware. In other word scope the use of the middleware to the first controller.
I could easily swap the order of router1
and router2
but my other requirement is because my router2
use in fact a route that catch all requests (/:id
) I need to have it last.
What I'm missing here and how can I do what I want ?
What I ultimely want is something along this:
/
|-test/
|-route // use middleware
|-something // use middleware
|-another // use middleware
...
|-:id // without middleware
That's why I have a router with many routes that are under router1
where I want the middleware.
And router2
with a catch-all without the middleware.
Upvotes: 2
Views: 208
Reputation: 713
As router1
and router2
are bound to a common route, they will be executed in order, along with their middlewares until one of the routers route matches with the requested path/route.
In your case, as you can't swap the order of the routers, you can create a middleware that wraps the router, checks if the requested path/route exists in the router and if so, returns it, otherwise just skip it.
var unlessMatch = function(router) {
let routerPaths = [];
// Retrieve, create a regex and store every route of the Router
router.stack.forEach(layer => {
if (layer.route) {
routerPaths.push(layer.route.path.replace(/\/?(:[^\/]+)(\/?)/g, "/[^\/]+"));
}
});
return function(req, res, next) {
// Check if requested route exists in the router
routerPaths.every(path => {
return new RegExp('^' + path + '(\/)?$').test(req.path) ? router(req, res, next) : true;
});
return next();
};
};
function middleware(req, res, next) {
console.log("middleware");
next();
}
function handler(req, res) {
console.log("OK");
res.status(200).send('OK');
}
const router1 = express.Router();
const router2 = express.Router();
// Bind middleware to router2
router2.use(middleware);
router1.get("/1", handler);
router2.get("/2", handler);
// Wrap router2 into unlessMatch middleware/function so that router2's middlewares
// are not executed if no route matches with the requested one
app.use("/test/", unlessMatch(router2), router1);
Upvotes: 1
Reputation: 2290
You can set a middleware when defining the routes:
router1.get("/1", middleware, handler);
router2.get("/2", handler);
The 1st will use the middleware and the second not.
BTW I would suggest the followings: do not create separate router for each route, only one is enough.
const router = express.Router();
router.get("/1", handler);
router.get("/2", handler);
app.use("/test/", router);
Upvotes: 1