Express - Allowing for a closing / at the end of a routing path - node.js

While routing in Express is quite straightforward I'm having trouble adjusting it to paths that end with a /.
For example, suggest I define the following route:
app.get('/about', (req,res) => res.render('about'));
Now if someone navigates to www.example.com/about the about view is rendered. However, if that same person navigates to www.example.com/about/ the route I specified above will not work. Some people (me included) have gotten used to naturally adding a closing / at the end of paths. I read the Express routing documentation page but it seems the developers were oblivious to this possibility. The only solution I've found thus far is to use regular expressions for each and every route to account for this variation. For example, the route above would become:
app.get(/\/about\/?/, (req,res) => res.render('about'));
Is there a more elegant (or built in) solution to allow for path with a closing / in Express?

This question has already been answered in https://stackoverflow.com/a/15773824/515774
Basically, you will need to add a middleware which will strip the trailing slash and make a redirect request, which will solve your problem.
Following is the code snippet from the previous answer.
app.use(function(req, res, next) {
if (req.path.substr(-1) == '/' && req.path.length > 1) {
var query = req.url.slice(req.path.length);
res.redirect(301, req.path.slice(0, -1) + query);
} else {
next();
}
});
To avoid redirect, you can just rewrite the URL. Reference https://stackoverflow.com/a/13446128/515774
Note: The browser URL stays the same using this approach.
app.use(function(req, res, next) {
if (req.url.slice(-1) === '/') {
req.url = req.url.slice(0, -1);
}
next();
});

Related

ExpressJS - Middleware Too Many Redirects

I am trying to use two middleware with my /app routes that checks for user authentication and then the status of their account. I have both middleware in place, but I am running into an endless redirect in instances where my req.session.accountStatus does not equal the conditions I have provided it. In general, I am trying to force the user to only have access to the page being redirected to. Am I using middleware in the wrong way? Is there a better approach?
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()){
return next();
}
res.redirect('/login');
}
function accountStatus(req, res, next) {
if(req.session.accountStatus == "active" || req.session.accountStatus == "trialing"){
return next();
} else {
//Endless loop. Need to fix
res.redirect('/app/settings/billing');
}
}
router.use(require('./site-routes'));
router.use('/app', isLoggedIn, accountStatus, require('./app-routes'));
It's probably easier to move the middleware to app-router.js.
So your main file would only do this:
router.use('/app', require('./app-routes'));
In app-routes.js, you first add the route for the URL that should be "open";
router.get('/settings/billing', ...);
Followed by the restrictive middleware:
router.use(isLoggedIn, accountStatus);
Followed by the rest of the routes.
That way, any requests for /app/settings/billing don't get passed through the middleware at all, and won't cause a redirect loop.
If isLoggedIn is mandatory for any route that starts with /app, you can use it in a similar way:
router.use(isLoggedIn);
router.get('/settings/billing', ...);
router.use(accountStatus);
router.get(...);

In App redirect in expressjs using middleware

I am trying to make a middleware for handling url aliases, what I am doing right now is :
// [...]
module.exports = function() {
return function(req, res, next) {
// getAlias would get an object {alias:"alias/path",source:"/real/path"} or null
var alias = getAlias(req.url);
if(alias) {
req.url = alias.source;
}
next();
};
};
So basicaly I am looking in a store for the requested url and if it is found as an alias I change request.url to the source path to that alias so that express calls the right route.
The problem is request.url and request.path have the same value, but changing request.path does not work while request.url works. In addition I am not sure which one i have to test agains.
Things work when I interact with request.url but just wanted to make sure that I am doing it the proper way.
Any thoughts ?
Rewriting the req.url property is the correct way for internally rerouting requests. That is why there is a req.originalUrl for the cases where one does change the original URL.
This is what the Express documentation states for req.originalUrl:
This property is much like req.url, however it retains the original
request url, allowing you to rewrite req.url freely for internal
routing purposes.
The req.url property isn't documented, but from the statement above you can infer it's meant to be used in the way you explained. It is also used in that way in some of the Express tests.
You can use run-middleware module exactly for that. Just run the handler you want by using the URL & method & data.
https://www.npmjs.com/package/run-middleware
For example:
module.exports = function() {
return function(req, res, next) {
// getAlias would get an object {alias:"alias/path",source:"/real/path"} or null
var alias = getAlias(req.url);
if(alias) {
res.runMiddleware(alias,(status,data)=>(res.status(status).send(data))
}
next();
};
};

How to Redirect to Single Page Web App in Express for Node

I am writing a website with a single page web app (the rest of the website is just static files which are served). I am trying to write a piece of middleware for express to redirect all requests that follow the pattern 'example.com/app' to 'example.com/app' so that requests such as 'example.com/app/my/specific/page/' will all result in the same page being sent. The key issue with this is that the url in the address bar of the browser must not change so that the javascript app itself can interpret it and display the correct thing.
I could have done something like this:
app.use( '/app', function ( req, res ) {
res.redirect('/app');
});
However, this causes the url of the page to change and a separate HTTP request is assumedly made.
The most obvious alternative solution is to do something like this:
app.use( '/app', function ( req, res ) {
res.sendfile(__dirname + '/public/app/index.html');
});
The issue here is that resources from the page after requests like 'example.com/app/my/specific/page/' will look in the wrong location. For example, if I have an image on the page such as then it will look for example.com/app/my/specific/page/image.jpg. Since no image is returned, it will not display on the page. This happens for all external scripts or stylesheets.
I also tried something like this:
app.use( '/app', function ( req, res ) {
res.sendfile(__dirname + '/public/beta' + url.parse(req.url).pathname);
});
but that was very stupid of me for obvious reasons.
In the end I used this middleware to serve the app's page when appropriate
// all unmatched requests to this path, with no file extension, redirect to the dash page
app.use('/dash', function ( req, res, next ) {
// uri has a forward slash followed any number of any characters except full stops (up until the end of the string)
if (/\/[^.]*$/.test(req.url)) {
res.sendfile(__dirname + '/public/dash/index.html');
} else {
next();
}
});
I then set used a base HTML element with the href attribute pointed to the root.
If you're still trying to accomplish this I may have found a starting point. Alexander Beletsky has a Backbone.js + Express SPA boilerplate repo Located Here.
For a brief article on how it came about you can read his article on Dzone.

Is it possible to apply basic authentication / middleware in on routes with a whitelist in Express?

I'm implementing a RESTful API with Express in Node, and I'm new to both. I'd like to use basic authentication to control access.
I would like to apply it using something like a whitelist but I'm not sure how to do that.
Blacklisting is easy, I can just pepper my #VERB calls with the second argument:
app.get('/', asyncAuth, requestHandler);
I can take that even further and blacklist everything with:
app.all('*', asyncAuth, requestHandler);
But I want to apply my basicAuth to every single route, except for POST /users. Is there an elegant way to do that? Can I use the 'blacklist' approach then selectively remove it from the routes I'd like? I couldn't figure out how.
Define your route for POST /users before the blacklisted routes:
app.post('/users', function(req, res) {
...
});
app.all('*', asyncAuth, requestHandler);
You could maintain a list of regexps that are whitelisted, and match the url against each url in the list, if it matches any then proceed, else require auth
app.all('*', asyncAuth);
function asyncAuth(req, res, next) {
var done = false;
whitelist.forEach(function(regexp) {
if (req.url.match(regexp)) {
done = true;
next();
}
});
if (!done) requireAuth(next);
}
Something along those lines

Node.js www - non www redirection

Is there a chance to somehow redirect www to non-www URLs in node.js? Since there is no htaccess in node web server I am curious how to do that.
Even though this question is nearly 3 years old, there are a few subtle issues with the previously posted answers (and their comments), as well as some good advice in the comments that didn't make it back into the answers themselves. Here's a few important things to note:
Don't hardcode the http:// or https:// in the redirect URI; this makes life suck when switching between dev and prod environments - use req.protocol instead.
Also note that in order to use req.protocol reliably behind a proxy performing SSL termination (such as Elastic Load Balancer), you need to make sure that the trust proxy setting is enabled. This will ensure that req.protocol returns the protocol that the browser sees, not the protocol that finally made it to your app server.
The accepted answer has a logic bug, as it matches on /^www/, but formats the redirect URI with /^www./. In practice that probably won't bite anyone, but it would result in infinite redirect loops for something like wwwgotcha.example.com.
Be sure to use req.headers.host instead of req.host, as the latter strips out the port number. So, if you were to handle a request for www.example.com:3000, you'd redirect the user to www.example.com, minus the correct port number.
As Dário pointed out, you'll typically want to use a 301 redirect when going from www to non-www (or vice versa) for SEO purposes.
The last issue is the most minor, but it's generally safer to use req.originalUrl when creating redirect URIs, just in case you happen to be running in a mounted "sub app".
All that being said, here's my recommended approach that takes the above into consideration:
function wwwRedirect(req, res, next) {
if (req.headers.host.slice(0, 4) === 'www.') {
var newHost = req.headers.host.slice(4);
return res.redirect(301, req.protocol + '://' + newHost + req.originalUrl);
}
next();
};
app.set('trust proxy', true);
app.use(wwwRedirect);
You're using express, right? If so you can make a route handler that all GET requests go through, checks if they're to a 'www' URL, and redirects to the appropriate non-www URL if appropriate.
app.get('/*', function(req, res, next) {
if (req.headers.host.match(/^www/) !== null ) {
res.redirect('http://' + req.headers.host.replace(/^www\./, '') + req.url);
} else {
next();
}
})
An updated version of jmar777's answer:
Using express
server.use((req, res, next) => {
if (req.headers.host.startsWith('www.')) {
const newHost = req.headers.host.slice(4)
return res.redirect(
301,
`${req.protocol}://${newHost}${req.originalUrl}`,
)
}
next()
})
This is a basic exemple of how you could mimic the behavior of the redirect directive of apache in nodejs.
The function redirect takes either a RegExp or a string.
var http, redirect;
http = require("http");
redirect = function(host, res, pattern, redirect){
if (host == pattern || (pattern instanceof RegExp && host.match(pattern))) {
console.log("Redirected " + host);
res.writeHead(302, {
'location': redirect
});
res.end();
}};
http.createServer(function(req, res){
redirect(req.headers.host, res, /^www/, 'http://plouf.url');
redirect(req.headers.host, res, 'www.plouf.url', 'http://plouf.url');
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.end('Hello World\n');
}).listen(8000, '127.0.0.1');
In the world of DevOps and the increasing adoption of hosted platforms, this type of thing should never be handled by code. You should handle the redirect using infrastructure instead. Putting this code into the application means that issues cannot be mitigated by the Operations team should something fail in the apex entry. In addition, while it is fancy and shiny and hip, using the apex as your site url limits the ability of Operations to redirect the site should something go wrong and causes issues when you want to do a blue/green, A/B testing or geo-redirection (in some cases). Your code and app should be url-agnostic.
I agree with Sebastian above with a minor tweak and if you are using Express. I would just make it a middleware it will be processed on all requests.
function removeWWW(req, res, next){
if (req.headers.host.match(/^www/) !== null ) {
res.redirect('http://' + req.headers.host.replace(/^www\./, '') + req.url);
} else {
next();
}
}
app.use(removeWWW);

Resources