Express dynamic route's get added to static routes middleware stack - node.js

I am defining static & dynamic routes in Express, and I have built a generic responder to send response to client. The responder is global to all routes and thus added at the end.
However, when I define static routes, matching dynamic routes' middlewares get added to the stack before the responder.
To illustrate:
server.get('/hello/test', function(req, res, next) {
console.log('/hello/test');
next();
});
server.get('/hello/:page', function(req, res, next) {
console.log('/hello/:page');
next();
});
server.use(function(req, res, next) {
res.status(200).send('test');
});
Calling curl localhost:3000/hello/test will console.log both '/hello/test' and '/hello/:page' before the responder middleware gets called. I only want the first matching routes middleware to be called.
Is there anyway to prevent this behaviour?

The method next() is passing the control to the next handler.
You could solve your issue just removing the next() call from your routes, and putting the middleware into a function:
server.get('/hello/test', myMiddleware(req, res, next), function(req, res) {
console.log('/hello/test');
});
server.get('/hello/test_b', myMiddleware(req, res, next), function(req, res) {
console.log('/hello/test_b');
});
server.get('/hello/:page', myMiddleware(req, res, next), function(req, res) {
console.log('/hello/:page');
});
server.get('*', myMiddleware(req, res, next), function (req, res) {
res.type('txt').send('Not found');
});
function myMiddleware(req, res, next) {
console.Log("in midleware")
next();
}

Related

Express - using application-level middleware conditionally on route-level

I'm trying to understand how to use an application-level middleware (or at least usually used like this) like cookie-parser on route-level and conditionally.
I tried something like:
const myMiddleware = (req, res, next) => {
if (myCondition) {
return cookieParser();
} else {
next();
}
}
app.use('/admin', myMiddleware, (req, res) => {
res.sendStatus(401)
})
But it's not working, the request will be just stuck.
Is this possible?
Traditional cookie-parser implementation:
app.use(cookieParser())
cookieParser() returns a middleware function, i.e. a function that takes in req, res, next as arguments. You just have to pass it the arguments:
const cookieParserMiddleware = cookieParser();
const myMiddleware = (req, res, next) => {
if (myCondition) {
return cookieParserMiddleware(req, res, next);
}
next();
};
app.use("/admin", myMiddleware, (req, res) => {
res.sendStatus(401);
});
Notice that I'm creating the cookieParser middleware outside myMiddleware - technically we could also just do return cookieParser()(req, res, next) but recreating the same middleware again and again on every request would be wasteful.
I've also removed the else since the if block returns from the function (guard clause).

Express trigger not found route for each route

I am using express with a pattern like this :
app = express();
router = express.Router();
router.use((req, res, next) => {
console.log("my middleware before");
next();
});
router.get('/foo', (req, res, next) => {
console.log("My route");
res.send("<h1>Hello</h1>")
next();
});
router.use((req, res, next) => {
console.log("my middleware after");
});
app.use("/", router);
app.get("*", (req, res, next) => {
console.log("page not found");
throw new Error("Not Found");
});
app.use((err, req, res, next) => {
console.log("Error occure");
res.send("<h1>Error</h1>");
});
app.listen(3000);
When I request '/foo' I would like to have
> my middleware before
> My route
> my middleware after
<h1>Hello</h1>
And when I request anything else :
> page not found
> Error occure
<h1>Error</h1>
But the page not found route is executed in each case, even if route '/foo' is done.
How can I get it working ?
When I run your code, I do not get the output you show, so something about your real code is apparently different than what you show in your question.
I do get a slightly confusing output and that happens because the browser sends both the /foo request and a /favicon.ico request. When I run it, the /foo request generates the desired output. The /favicon.ico request generates some middleware output and then gets stuck in the router.
If you filter out the /favicon.ico route (so that it doesn't confuse things) by adding this as the first route:
app.get("/favicon.ico", (req, res) => {
res.sendStatus(404);
});
Then, I get exactly this output in the server logs when I request /foo:
my middleware before
My route
my middleware after
Which is exactly what you asked for.
There is, however, a general problem with this:
router.use((req, res, next) => {
console.log("my middleware after");
});
Because it will catch and hang any legit requests that haven't yet had a response sent. You can't really code it that way unless you only don't call next() if a response has already been sent.
As a bit of a hack, you could do this:
router.use((req, res, next) => {
console.log("my middleware after");
// if response hasn't yet been sent, continue routing
if (!res.headersSent) {
next();
}
});
But, there is probably a better way to solve whatever problem you're actually trying to solve. If, in the future, you describe your real problem rather than a problem you have with your solution, then you allow people to offer a wider range of solutions to your real problem including things you haven't even thought of to try. As your question is written right now, we're stuck down the solution path you followed and don't know what the original problem was. That is, by the way, referred to as an XY Problem.
Do this
app = express();
router = express.Router();
router.use((req, res, next) => {
console.log("my middleware before");
next();
});
router.get('/foo', (req, res, next) => {
// use locals to record the fact we have a match
res.locals.hasMatch = true
console.log("My route");
res.send("<h1>Hello</h1>")
next();
});
router.use((req, res, next) => {
console.log("my middleware after");
});
app.use("/", router);
app.get("*", (req, res, next) => {
console.log("page not found");
throw new Error("Not Found");
});
app.use((err, req, res, next) => {
// check locals to see if we have a match
if (!res.locals.hasMatch) {
console.log("Error occure");
res.send("<h1>Error</h1>");
}
});
app.listen(3000);
You can utilize middlewares and even nest them.
You can implement it like this:
Middlewares
const before = (req, res, next) => {
console.log("my middleware before");
next(); // Supply next() so that it will proceed to the next call,
// in our case, since this is supplied inside the router /foo, after this runs, it will proceed to the next middleware
};
const after = (req, res, next) => {
console.log("my middleware after");
};
Route
// Supply "before" middleware on 2nd argument to run it first when this route is called
router.get('/foo', before, (req, res, next) => {
console.log("My route");
res.send("<h1>Hello</h1>");
next(); // Call next() to proceed to the next middleware, or in "after" middleware
}, after); // Supply the "after" middleware
Once ran, it will proceed with this desired result sequence:
> my middleware before
> My route
> my middleware after
Unmatched Routes Handler
Instead of this
app.get("*", (req, res, next) => {
console.log("page not found");
throw new Error("Not Found");
});
You can implement it like this instead, this is after your app.use("/", router); -- This will handle your unmatched routes:
Sources:
https://stackoverflow.com/a/44540743/6891406
https://stackoverflow.com/a/16637812/6891406
app.use((req, res, next) => {
console.log("page not found");
res.json({ error: 'Page not Found' })
});

Using express middleware only for a GET request

The way I understand it, if I do something like:
app.use('/something', function(req, res, next) {
// some content here
});
This basically means that if there's a request to 'something', then the middleware (my function) is executed before the next function.
So if I have something like this to handle a GET request,
app.get('/something', function(req, res, next) {
console.log('hello');
});
Then 'hello' is going to be printed out after my original function has finished executing.
But how do I make it so that my middleware function is just executed when I ONLY make a GET request and not a POST request?
For a GET only middleware, just do the following
// Get middleware
app.get('/something', function(req, res, next) {
console.log('get hello middleware');
next();
});
// GET request handler
app.get('/something', function(req, res) {
console.log('get hello');
res.end();
});
// POST request handler
app.post('/something', function(req, res) {
console.log('post hello');
res.end();
});
app.post('/something', your_middleware, function(req, res, next) {
console.log('hello');
});
Only during the post request your_middleware will be executed.
your_middleware should be a function as follows:
function(req, res, next){
....
next()
}
you can pipe in as many middlewares you want in this way for a specific route and request type

How do i send multiple arguments to an Express.js route?

Let say I want to add multiple arguments.
Here's the code
function firstArgument(req, res, next) {
// Do something
}
function secondArgument(req, res, next) {
// Do something
}
app.get('/something', firstArgument, secondArgument, function(req, res, next) {
// Is it possible to do this?
});
Is it possible? if so how does it works? Can anyone explain it to me.
Thank you
All the answers are in the express docs - http://expressjs.com/es/guide/routing.html
To summarize, for your scenario you can use:
var cb0 = function (req, res, next) {
console.log('CB0')
next()
}
var cb1 = function (req, res, next) {
console.log('CB1')
next()
}
app.get('/example/d', [cb0, cb1], function (req, res, next) {
console.log('response will be sent by the next function ...')
next()
}, function (req, res) {
res.send('Hello from D!')
})
or, without the second method.
var cb0 = function (req, res, next) {
console.log('CB0')
next()
}
var cb1 = function (req, res, next) {
console.log('CB1')
next()
}
app.get('/example/d', [cb0, cb1], function (req, res) {
res.send('Hello from D!')
})
Regarding how it works - it simply runs all the methods one after the other: when the next() method is called, the next method is being called.

Express - public directory divided for authorized/unauthorized users

I have an app written in express.js and I'm trying to divide this application to 2 sections:
one for unauthorized users (with routes only to / - landing page, /login and /* - error404)
and second (routes will be: / - landing page, /app/* - angular SPA which will handle routing on its own)
Express is also configured to take static files from /unauth/public/
And I want to add second static folder for request from authorized routes - /auth/public
which goes to /app/*
My route config looks like this:
var authRoutes = express.Router();
var unauthRoutes = express.Router();
authRoutes.get('/app/*', function(req, res, next) {
if(!req.isAuthenticated())
return res.redirect("/login/");
res.send("AUTHORIZED");
});
unauthRoutes.get('/', function(req, res, next) {
res.send("LANDING PAGE");
});
unauthRoutes.get('/login/', function(req, res, next) {
if(req.isAuthenticated())
return res.redirect("/app/");
res.send("LOGIN PAGE");
});
unauthRoutes.get('/registration/', function(req, res, next) {
if(req.isAuthenticated())
return res.redirect("/app/");
res.send("REGISTRATION PAGE");
});
unauthRoutes.get('/*', function(req, res, next) {
res.send("ERROR 404");
});
app.use('/', authRoutes);
app.use('/', unauthRoutes);
I tried to modify req.url and call another static oruter express.static('auth/public') based on this:
Using express.static middleware in an authorized route
But I don't know, how to handle route app.get('/auth/*', ...) - previous modification will replace url and this route will never be called..
You could try something like this:
// Create your static middlewares
var unauthStatic = express.static('unauth/public');
var authStatic = express.static('auth/public');
// This goes in place of where you would normally load your static middleware
app.use(function(req, res, next) {
if (req.isAuthenticated()) {
authStatic(req, res, next);
} else {
unauthStatic(req, res, next);
}
});
edit:
if you want authenticated users to be able to access files from both the auth and unauth directories, you can make two calls to app.use, like this:
app.use(unauthStatic);
app.use(function(req, res, next) {
if (! req.isAuthenticated()) {
return next();
}
authStatic(req, res, next);
});
Remember that express uses middleware in a stack, meaning to serve a given request, all registered middleware is used in the order it's used. Once a bit of middleware calls req.send, no further middleware gets executed. Anyway, try something like this:
function Authorization(req, res, next) {
if(!req.isAuthenticated())
return res.redirect("/login");
next();
}
var AnonRouter = express.Router()
// GET /style.css will request /unauth/public/style.css
.use(express.static('unauth/public'))
.get('/', function (req, res) { })
.get('/login', function (req, res) { });
var AuthRouter = express.Router()
.use(Authorization)
// GET /app/style.css will request /auth/public/style.css
.use(express.static('auth/public'))
.get('*', function (req, res, next) {
// Handle reqs for non-static files
});
app.use('/', AnonRouter);
app.use('/app', AuthRouter);
app.get('*', function (req, res) {
res.status(404).send('404!');
});
But I don't know, how to handle route app.get('/auth/*', ...) - previous modification will replace url and this route will never be called..
This statement makes me think that you are trying to somehow handle the request after express's static middleware has been called. This is not possible: the static middleware serves static files and you cannot execute additional logic after it does so, but you can run stuff before! Note that in my code, the Authorization middleware will run before the static file is sent.

Resources