Wednesday, August 30, 2017

ExpressJS router normalized/canonical urls

Leave a Comment

I'm after normalized/canonical urls for SPA with ExpressJS server.

Although it is SPA that is backed up by server side router - templates can differ a bit for app urls. One of the differences is <link rel="canonical" href="https://example.com{{ originalPath }}"> tag. Not the relevant detail but explains the context of the question. I expect that there will be only one URL that responds with 200, its variations are redirected to it with 301/302 (works for living humans and search engines).

I would like to make the urls case-sensitive and strict (no extra slash), similarly to Router options, but non-canonical urls (that differ in case or extra slash) should do 301/302 redirect to canonical url instead of 404.

In most apps I just want to force the urls for * routes to be lower-cased (with the exception of queries), with no extra slashes. I.e. app.all('*', ...), and the redirects are:

/Foo/Bar/ -> /foo/bar /foo/Bar?Baz -> /foo/bar?Baz 

But there may be exceptions if the routes are defined explicitly. For example, there are camel-cased routes:

possiblyNestedRouter.route('/somePath')... possiblyNestedRouter.route('/anotherPath/:Param')... 

And all non-canonical urls should be redirected to canonical (parameter case is left intact):

/somepath/ -> /somePath /anotherpath/FOO -> /anotherPath/FOO 

The logic behind canonical urls is quite straightforward, so it is strange that I couldn't find anything on this topic regarding ExpressJS.

What is the best way to do this? Are there middlewares already that can help?

3 Answers

Answers 1

I have looked for npms, I could not find any, so this scratched my mind and I've coded a small task for express to do for each request, which seem to work fine. Please add this to your code.

var urls = {   '/main' : '/main',   '/anotherMain' : '/anotherMain' }  app.use(function(req, res, next){    var index = req.url.lastIndexOf('/');    //We are checking to see if there is an extra slash first   if(req.url[index+1] == null || req.url[index+1] == undefined || req.url[index+1] == '/'){      //slashes are wrong      res.send("please enter a correct url");      res.end();   }else{        for(var item in urls){          if(req.url != item && req.url.toUpperCase() == item.toUpperCase()){            res.redirect(item);            console.log("redirected");            //redirected          }else if (req.url == item) {            console.log("correct url");            next();          }else{            //url doesn't exist          }      }    }   next();  });  app.get('/main', function(req, res){   res.render('mainpage'); });  app.get('/anotherMain', function(req, res){   res.send("here here"); }); 

USAGE

All you have to do is, add your urls to urls object like done above with giving it the same key value. That's it. See how easy it is. Now all of your clients request will be redirected to the correct page case sensitively.

UPDATE

I have also made one for POST requests, I think it is pretty accurate, you should also give it a try. Ff you want a redirect when the user mixes up slashes, you need to write some regex for it. I didn't have time also my brain was fried so I made a simple one. You can change it however you like. Every web structure has its own set of rules.

var urlsPOST = {   '/upload' : '/upload' }  app.use(function(req, res, next){    if(req.method == 'POST'){      var index = req.url.lastIndexOf('/');      if(req.url[index+1] == null || req.url[index+1] == undefined || req.url[index+1] == '/'){         //slashes are wrong        res.sendStatus(400);        res.end();        return false;      }else{        for(var item in urlsPOST){           if(req.url != item && req.url.toUpperCase() == item.toUpperCase()){             res.redirect(307, item);             res.end();             return false;             //redirected            }else if (req.url == item) {             console.log("correct url");             next();            }else{             res.sendStatus(404).send("invalid URL");             return false;             //url doesn't exist           }       }     }   }   next(); }); 

Answers 2

You probably want to write your own middleware for this, something along the lines of this:

app.set('case sensitive routing', true);    /* all existing routes here */    app.use(function(req, res, next) {    var url = find_correct_url(req.url); // special urls only    if(url){      res.redirect(url); // redirect to special url    }else if(req.url.toLowerCase() !=== req.url){      res.redirect(req.url.toLowerCase()); // lets try the lower case version    }else{      next(); // url is not special, and is already lower case    };  });

Now keep in mind this middleware can be placed after all of your current routes, so that if it does not match an existing route you can try to look up what it should be. If you are using case insensitive route matching you would want to do this before your routes.

Answers 3

Using the same code as @user8377060 just with a regex instead.

  // an array of all my urls   var urls = [     '/main',     '/anotherMain'   ]    app.use(function(req, res, next){      var index = req.url.lastIndexOf('/');      //We are checking to see if there is an extra slash first     if(req.url[index+1] == null || req.url[index+1] == undefined || req.url[index+1] == '/'){      //slashes are wrong      res.send("please enter a correct url");      res.end();     }else{        for(var item in urls){          var currentUrl = new RegExp(item, 'i');           if(req.url != item && currentUrl.test(item)){            res.redirect(item);            console.log("redirected");            //redirected          }else if (req.url == item) {            console.log("correct url");            next();          }else{            //url doesn't exist          }      }      }     next();    }); 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment