node.js expressjs pattern match not equal - node.js

I'm using expressjs with node and running both https and http.
I want to require that all routes for /secure/* use https. This is done:
app.all("/secure/*", function(req, res, next) {
if (!req.connection.encrypted) {
res.redirect("https://" + req.headers["host"].replace(new RegExp(config.http_port, "g"), config.https_port) + req.url);
} else {
return next();
};
});
However, I also want to require that all routes that are not using /secure/* and try to access https, are redirected using the same method to http.
I tried doing this:
app.all("*", function(req, res, next) {
console.log(req);
if (req.connection.encrypted) {
res.redirect("http://" + req.headers["host"].replace(new RegExp(config.https_port, "g"), config.http_port) + req.url);
} else {
return next();
};
});
But I end up in a redirect loop when accessing the https pages. Is there a way to specify all routes, except those with /secure/* ?
Thank you!

A simple solution to your problem is:
app.all("*", function(req, res, next) {
if (req.connection.encrypted && !/^\/secure/.test(req.url)) {
res.redirect("http://" + req.headers["host"].replace(new RegExp(config.https_port, "g"), config.http_port) + req.url);
} else {
return next();
};
});
Only do the redirect if the URL doesn't start with /secure.
However, I'd propose that instead of the redundant 'secure' label in the URLs, just mark certain paths as requireHTTP or requireHTTPS. You know you can pass multiple methods into app.get and other such router methods, right? Assuming you define requireHTTP and requireHTTPS (which would be identical to your original functions), you'd just do:
app.get("/path/to/keep/encrypted", requireHTTPS, function(req, res) {
// Rest of controller
});
app.get("/path/to/keep/public", requireHTTP, function(req, res) {
// Rest of controller
});
app.get("/path/you/dont/care/about/encryption/status", function(req, res) {
// Rest of controller
});
That should do it.

Related

Nodejs express res.redirect loop

Using Nodejs and an Express server I'm trying to prevent anyone from reaching my second set of routes without logging in and while this works I get stuck in a redirect loop if the session doesn't detect the email in the session token. I believe its trying to check for the req.session.email for the /users endpoint as well causing the redirect loop but as the session checking middleware is used after I thought the /users endpoints would avoid the check.
How can I organize my code so that the books endpoints can only be reached when the req.session.email is satisfied and also not get stuck in a redirect loop when someone tries to reach it without being logged in?
app.use('/users', users)
app.use((req, res, next) => {
if(!req.session.email){
res.redirect('/login')
}
else{
next();
}
})
app.use('/books', books);
The order of the app.use statements is not really important in this case; You could add your middleware to the route-level if you're only checking the /books endpoint.
const yourMiddlewareFunction = (req, res, next) => {
if(!req.session.email){
res.redirect('/login')
}
else{
next();
}
}
app.use('/books', yourMiddlewareFunction, books);
If you only want to protect the /books endpoints, you can do it like this :
function requireLogin(req, res, next) {
if(!req.session.email){
res.redirect('/login')
}
else{
next();
}
}
app.use('/books', requireLogin, books);

Static routing in node express

Im using a set of static routes in Node Express and are experiencing a very strange phenomenon.
The routing is set up so that '/list/*' and '/setup/*' gets to different html-files where the directories are used kind of as storage id, for example updating info on the page url/setup/12345 would store info in 12345.
To be able to load scripts and such there is also route with regex matching /assets/ to allow url/setup/assets/script.js to be reached without routing to setup.html with a new storage id.
My problem is that this works for url/setup/assets/script.js but not for url/list/assets/script.js even though they have identical routings.
Edit:
Navigating to url/list/assets/script.js leads to list.html (unwanted behaviour)
Navigating to url/setup/assets/script.js leads to script.js (wanted behaviour)
Any ideas on why '/list/*'` wont work?
Here are my static routes:
app.use(/assets/, express.static(wwwPath));
app.use('/list/*', function(req, res, next) {
res.sendFile('list.html', { root: wwwPath });
});
app.use('/setup/*', function(req, res, next) {
res.sendFile('setup.html', { root: wwwPath });
});
The solution was to use custom middleware. Here are the new routes:
var requestParser = function(req, res, next) {
if(req.originalUrl.indexOf('/assets/') >= 0) {
var assetPath = path.join(wwwPath, req.path.slice(req.url.indexOf('/assets/')));
fs.stat(assetPath, function(error, stat){
if(stat && stat.isFile()) {
res.sendFile(assetPath);
}
else{
res.status(404).send('<h1>404</h1>');
}
});
}
else {
next();
}
};
app.use(requestParser);
app.use('/list/*', function(req, res, next) {
res.sendFile('schema.html', { root: wwwPath });
});
app.use('/setup/*', function(req, res, next) {
res.sendFile('setup.html', { root: wwwPath });
});

Express routing GET with search params

I have two GET routes for get stores but, one route is for get all stores and the other route is for get just nearby stores.
1) The url request for get all stores is as follows:
http://mydomain/stores
2) The url for get all nearby stores:
http://mydomain/stores?lat={lat}&lng={lng}&radius={radius}
The question is:
How can I map those urls properly in Express, in a way to redirect each route to the corresponding method?
app.get('/stores', store.getAll);
app.get('/stores', store.getNear);
app.get('/stores', function(req, res, next){
if(req.query['lat'] && req.query['lng'] && req.query['radius']){
store.getNear(req, res, next);
} else {
store.getAll(req, res, next)
};
});
edit - a second way to do it:
store.getNear = function(req, res, next){
if(req.query['lat'] && req.query['lng'] && req.query['radius']){
// do whatever it is you usually do in getNear
} else { // proceed to the next matching routing function
next()
};
}
store.getAll = function(req, res, next){
// do whatever you usually do in getAll
}
app.get('/stores', store.getNear, store.getAll)
// equivalent:
// app.get('/stores', store.getNear)
// app.get('/stores', store.getAll)

forwarding to another route handler without redirecting in express

I have the following code :
app.get('/payment', function(req, res) {
// do lots of stuff
});
now I want to add the following :
app.post('/payment', function(req, res) {
req.myvar = 'put something here';
// now do the same as app.get() above
});
Obviously I want to reuse the code. I tried doing next('/payment') inside the post handler and put it above the get handler, but no luck, probably because they are different VERBs.
What are my options ?
Thanks.
Just lift out the middleware to its own function and use it in both routes.
function doLotsOfStuff (req, res) {
// do lots of stuff
}
app.get('/payment', doLotsOfStuff);
app.post('/payment', function(req, res, next) {
req.myvar = 'put something here';
next();
}, doLotsOfStuff);

Forward request to alternate request handler instead of redirect

I'm using Node.js with express and already know the existence of response.redirect().
However, I'm looking for more of a forward() functionality similar to java that takes the same parameters as redirect, but internally forwards the request instead of having the client perform the redirect.
To clarify, I am not doing a proxy to a different server. I'd like to forward('/other/path') directly within the same app instance
It wasn't apparently obvious how to do this from the express documentation. Any help?
You just need to invoke the corresponding route handler function.
Option 1: route multiple paths to the same handler function
function getDogs(req, res, next) {
//...
}}
app.get('/dogs', getDogs);
app.get('/canines', getDogs);
Option 2: Invoke a separate handler function manually/conditionally
app.get('/canines', function (req, res, next) {
if (something) {
//process one way
} else {
//do a manual "forward"
getDogs(req, res, next);
}
});
Option 3: call next('route')
If you carefully order your router patterns, you can call next('route'), which may achieve what you want. It basically says to express 'keep moving on down the router pattern list', instead of a call to next(), which says to express 'move down the middleware list (past the router)`.
You can implement forward (aka rewrite) functionality by changing request url property and calling next('route').
Note that the handler performing forward needs to be configured before other routes which you perform forwards to.
This is example of forwarding all *.html documents to routes without .html extension (suffix).
function forwards(req, res, next) {
if (/(?:.+?)\.html$/.test(req.url)) {
req.url = req.url.replace(/\.html$/, '');
}
next('route');
}
You call next('route') as the last operation. The next('route') passes control to subsequent routes.
As mentioned above, you need to configure forwards handler as one of the first handlers.
app.get('*', forwards);
// ...
app.get('/someroute', handler);
The above example will return the same content for /someroute as well as /someroute.html. You could also provide an object with a set of forward rules ({ '/path1': '/newpath1', '/path2': '/newpath2' }) and use them in forward mechanism.
Note that regular expression used in forwards function is simplified for mechanism presentation purposes. You would need to extend it (or perform check on req.path) if you would like to use querystring parameters etc.
I hope that will help.
For Express 4+
Using the next function does not work if the next handler is not added in the right order. Instead of using next, I use the router to register the handlers and call
app.get("/a/path", function(req, res){
req.url = "/another/path";
app.handle(req, res);
}
Or for HTML5 mode of React/Angular
const dir = process.env.DIR || './build';
// Configure http server
let app = express();
app.use('/', express.static(dir));
// This route sends a 404 when looking for a missing file (ie a URL with a dot in it)
app.all('/*\.*', function (req, res) {
res.status(404).send('404 Not found');
});
// This route deals enables HTML5Mode by forwarding "missing" links to the index.html
app.all('/**', function (req, res) {
req.url = 'index.html';
app.handle(req, res);
});
Using the next function does not work if the next handler is not added in the right order. Instead of using next, I use the router to register the handlers and call
router.get("/a/path", function(req, res){
req.url = "/another/path";
router.handle(req, res);
}
Express 4+ with nested routers
Instead of having to use the outside of route/function app, you can use req.app.handle
"use strict";
const express = require("express");
const app = express();
//
// Nested Router 1
//
const routerOne = express.Router();
// /one/base
routerOne.get("/base", function (req, res, next) {
res.send("/one/base");
});
// This routes to same router (uses same req.baseUrl)
// /one/redirect-within-router -> /one/base
routerOne.get("/redirect-within-router", function (req, res, next) {
req.url = "/base";
next();
});
// This routes to same router (uses same req.baseUrl)
// /one/redirect-not-found -> /one/two/base (404: Not Found)
routerOne.get("/redirect-not-found", function (req, res, next) {
req.url = "/two/base";
next();
});
// Using the full URL
// /one/redirect-within-app -> /two/base
routerOne.get("/redirect-within-app", function (req, res, next) {
req.url = "/two/base";
// same as req.url = "/one/base";
//req.url = req.baseUrl + "/base";
req.app.handle(req, res);
});
// Using the full URL
// /one/redirect-app-base -> /base
routerOne.get("/redirect-app-base", function (req, res, next) {
req.url = "/base";
req.app.handle(req, res);
});
//
// Nested Router 2
//
const routerTwo = express.Router();
// /two/base
routerTwo.get("/base", function (req, res, next) {
res.send("/two/base");
});
// /base
app.get("/base", function (req, res, next) {
res.send("/base");
});
//
// Mount Routers
//
app.use("/one/", routerOne);
app.use("/two/", routerTwo);
// 404: Not found
app.all("*", function (req, res, next) {
res.status(404).send("404: Not Found");
});
app.get('/menzi', function (req, res, next) {
console.log('menzi2');
req.url = '/menzi/html/menzi.html';
// res.redirect('/menzi/html/menzi.html');
next();
});
This is my code:when user enter "/menzi",the server will give the page /menzi/html/menzi.html to user, but the url in the browser will not change;
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:
app.runMiddleware('/get-user/20',function(code,body,headers){
res.status(code).send(body)
})

Resources