I have an app where certain pages require that the user be logged in.
I am not sure if there is something built in for this, but what I have for doing this is as follows:
app.use((req, res, next) => {
if (req.session.username) {
app.get('/project/create', projectCtrl.create)
app.get('/project/create/save', projectCtrl.save)
} else {
return res.redirect('/')
}
next()
})
Is this the correct way of doing this, or is there a better way in express? The way I am doing it kind of feels a little hacky.
Yes, that's one correct way of doing it. What you have is an application-level middleware in express. It gets called for every request the application receives.
You can extract the username check and apply that as a route middleware substack. This way the middleware only gets executed for the routes it's applied to.
function gatePass(req, res, next) {
if(req.session.username) {
next();
}
else {
return res.redirect('/');
}
}
app.get('/project/create', gatePass, projectCtrl.create)
app.get('/project/create/save', gatePass, projectCtrl.save)
You can take this a bit further if you'll like to separate concerns by using express router together with route-level middleware. This also applies a middleware directly to the routes.
var router = express.Router();
router.use('/project/create', gatePass);
router.use('/project/create/save', gatePass);
router.get('/project/create', projectCtrl.create);
router.get('/project/create/save', projectCtrl.save);
app.use('/', router);
this solution work. It's not the best but for small project it will be good. The only drawback is that you will need to define every route you want to be check with a session.
Nodejs is the world of middleware, so why not use one? I think it's the best thing to do.
Verify is a file where I export my middleware and I apply it on all my router.. (in this case it's just to check if the user is logged or not)
var verify = require('./verify');
router.all('/*', verify.isLogged, function(req, res, next) {
if(req.decoded._doc.isLogged == "") {
next();
}
else {
res.json("error");
}
});
This way, if in the future you need to check one thing, then another one, you will just need to call you function where you want to check
router.get('/test', verify.isLogged, verify.isAdmin function(req, res, next) {
if(req.decoded._doc.isAdmin == "") {
next();
}
else {
res.json("error");
}
});
Related
I have a server that is fully functioning, but I only want it to be accessable when I say. I do this via a discord bot which works fine. I currently have a boolean variable server_on and an if (server on) { do } in all of my app.get and app.post functions. Is there a cleaner way to do this, or is this if statement in every function the only way?
Edit:
Final working code
var block_server_middleware = function (req, res, next) {
if (!server_on) { res.send("server is currently unavailable") }
else { next() }
}
app.use(block_server_middleware)
and the other app.get and app.post functions were not changed at all
This was the only few lines added that made the whole idea work
You can define one middleware function that goes before all your routes are defined:
app.use((req, res, next) => {
if (!server_on) {
// stop all routing
res.send("server not on!");
} else {
// let routing continue
next();
}
});
This will keep your routes from ever getting run until server_on is back to true. If you have any routes you want to leave open all the time, then just place them before this middleware.
You can replace res.send("server not on!"); with whatever is appropriate for your use. You can return a single web page or you can send back a 4xx or 5xx error status (perhaps a 503 error).
I am trying to access my application-level middleware from router in a project generated with express application generator.
Middleware is used to query database with user ID received from router.
I feel like I'm missing something very simple (or fundamental) but can't get around the problem (this being my first Node.js project). So more than best practice I'm looking for a simple solution
I've tried using different app methods including post.
/app.js
var MyAppMidW = function (req, res, next) {
res.send(queryDB(req));
next()
}
app.use(MyAppMidW);
/routes/index.js
router.get("/dbquery", (req, res) => {
if (req.header('auth-header')) {
res.send(req.app.get.MyAppMidW(req.header('auth-header'))); //The problem
}
else {
res.send(req.app.get('defaultData')); //This works
}
});
Error messages include "$middleware is not a function" and "$middleware is not defined".
Solution
/app.js
app.MyAppMidW = function (req) {
queryDB(req);
}
/routes/index.js
router.get("/dbquery", (req, res) => {
if (req.header('auth-header')) {
req.app.MyAppMidW(req.header('auth-header'))); //Makes a database query
res.send(req.app.get('defaultData')); //Fetches database query result
}
else {
res.send(req.app.get('defaultData'));
}
});
If you do it like this
app.use(MyAppMidW);
Every request will query your db, and thats not what you want. I guess you use the MVC design pattern.
In your route folder you have something like this:
import appController from "../controllers/app.js"
router.get("/dbquery", appController.MyAppQuery)
And in your controllers folder you have your logic that querys the db
exports.MyAppQuery = (req, res){
//If u use mongodb for example
YourModel.find().then(data => {
res.json(data)
})
}
You need to call app.set("MyAppMidW", MyAppMidW) and then you can use get. Or do this inside the app.js file
app.MyAppMidW = function (req, res, next) {
res.send(queryDB(req));
next()
}
Then call it by req.app.get('MyAppMidW')(req.header('auth-header')) or req.app.MyAppMidW(req.header('auth-header')) inside the routes file.
But middleware is called automatically when you say app.use(MyAppMidW) the function is called by default on each request. So no need to call it explicitly inside the router function.
I am creating a user management system - However I am current finding myself checking the user type on a per router bases.
router.get('/admin/settings', (req, res) => {
if(admin) {
//Proceed.
}
}
router.get('/admin/users', (req, res) => {
if(admin) {
//Proceed.
}
}
Is there a better way of doing this? Can't I just set a route like this?
router.get('/admin*', (req, res) => {
if(!admin) {
res.status(404).send('Not found')
}
}
(I have tried and not succeeded, feels like it clashes with other routes)
Also, on a similar note. How Am I supposed to handle denying a user access to a script? Do I send a 404 or 403?
You can use an Express middleware function:
router.use(function(req, res, next) {
if(admin) {
return next();
}
// We fail closed
return res.status(403).send('Forbidden');
});
// This won't get called if the middleware doesn't call next()
router.get('/admin/settings', (req, res) => {
// Do stuff
}
Here, we call next() only if the user is an admin, which allows the call to continue. Any routes added after this middleware will be protected.
Also, on a similar note. How Am I supposed to handle denying a user access to a script?
A 403 is the appropriate code here, though a 404 can also be used if you wish to hide the route from unauthorized clients. I would suggest reading up on what each code is designed for.
I'm using express framework 4.0 for my node js server . I was wondering if there was any way to remove routes dynamically at runtime
var express = require('express');
var router = express.Router(/*Options */);
router.get('/', function (req, res)
{
res.render('index', {title: "Home"});
});
router.get('/features', function (req, res)
{
res.render('features', {title: "Features"});
});
//Hook into the routing system
module.exports = function(app,rootPath)
{
app.use(rootPath, router);
};
This is a trivial example, but how could I remove the /features path from the routing table?Additionally is it possible to overwrite this routing path with another should I wish to update the features routing path at a later date ?
AFAIK you can't delete a route dynamically (at least not in a nice way), but you can use a filtering middleware to disallow access to a route when a certain condition is set.
For example:
var allowRoute = true;
var filterMiddleware = function(req, res, next) {
if (allowRoute !== true) {
return res.status(404).end();
}
next();
};
app.get('/features', filterMiddleware, function(req, res) {
res.render('features', { title: 'Features' });
});
You toggle allowRoute to enable or disable access to the route (obviously, depending on the exact use case you could also use properties in req to enable/disable access to the route).
A similar setup could be used to overwrite the route handler with another one, although I'm beginning to wonder what you are trying to accomplish and if overwriting route handlers is the solution for that.
I have a Jade file that all of my templates extend called layout.jade. In it I want to be able to have a logout button if the user is currently logged in (this is kept track of in req.session).
So layout.jade will have something like,
-if (loggedin)
a.navButton(href="/logout") Log Out
The route for a page would look something like,
app.get("/foo", function(req, res) {
res.render("foo", {loggedin: req.session.isValidUser});
});
The thing is, I don't want to have to populate the loggedin variable manually in every single route. Is there a way I can use Express middleware to automatically set some default options for the object sent to res.render? Or is there a better method to do this?
Basically I'm asking how I can have some set of variables always sent to templates, as well as the ability to have certain custom variables available in certain templates by manually setting them in the routes.
It seems this is actually a documented feature I just had trouble finding, unless anyone has a better way of doing it; From the latest Express documentation,
app.locals: Application local variables are provided to all templates
rendered within the application. This is useful for providing helper
functions to templates, as well as app-level data.
So in my login success function has,
req.session.username = profile.username;
app.locals.username = profile.username;
My logout function,
app.get('/logout', function (req, res) {
delete app.locals.username;
req.session.destroy();
res.redirect('/login');
});
And finally in layout.jade/all of my templates,
- if(username)
a.navButton(href="/logout") Logout
If you set res.locals.loggedin in the /login route, as hexacyanide suggests, this local will not be available in the route for /foo. res.locals is cleared upon every request.
you could instead try placing this above other routes:
app.all('*', function(req, res, next){
if(req.user){
res.locals.loggedin = true;
res.locals.currentuser = req.user;
};
next();
});
Pretty sure that if you modify req.user during your route, the res.locals.currentuser that you set before won't updated to be the new req.user. but not certain about that.
I actually use a custom render function for each page where I render a template, it looks like this:
function myRender(templateName){
return function(req, res){
res.locals.currentuser = req.user || null;
res.render(templateName);
};
};
and I use it like this:
app.get('/foo'
, function(req, res, next){
res.locals.bar = req.query['bar'] || "";
console.log('passing request to myRender middleware');
next();
}
, myRender('foo-jade-template')
)
This has the advantage of only setting res.locals.currentuser when I am ready to render something, instead of before executing my route. So if I change req.user it is guranteed to have the most recent version at render time.
There is a line of code that is rather useful to you in the Express source:
// merge res.locals
options._locals = self.locals;
Therefore, when you run res.render(), Express will also take any locals that are stored in res.locals and pass them into the render. Therefore, all you have to do is set res.locals.loggedin to true, and then run res.render() as usual.
app.get('/login', function(res, res) {
res.locals.loggedin = true;
});
app.get('/foo', function(req, res) {
res.render('foo', {});
});