I'm very much a newbie to the whole express routing logic (and node and js for that matter). But I have problem that I can't seem to trace, but I think I understand the context of it. Well now, that didn't make a lot of sense, here it goes anyway.
I'm trying to skip through middleware conditionally based on data in the req.query and it renders the intended form page fine except for the fact that I get 'Cannot send header twice ...' in the console from the previous middleware, which i thought i skipped.
From what I can gather next() should get me to the next middleware instantly and skip the rest of the block entirely (kind of like how a return kicks you out of a function). Is this not correct?
path: localhost:3000/JohnnyBoy?action=form
router.get('/:name/', function(req, res, next) {
if(req.query.action !== 'view') { next(); };
console.log('Why am I seeing this in the console?');
res.render('first', {
title: req.params.name
});
});
router.get('/:name/', function(req, res, next) {
res.render('form', {
title: req.params.name + ' This is a form page',
formFields: fields.userPostFields()
});
});
You need to put return; after calling next(); only calling next() will not stop execution of the current function.
Replace
if(req.query.action !== 'view') { next(); };
with
if(req.query.action !== 'view') { next(); return;};
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 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 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");
}
});
I'm trying to understand JWT and how they work with Node and Express .js. I have this middleware that tries to authenticate users with a token:
app.use(function(req, res, next) {
if(req.headers.cookie) {
var autenticazione = req.headers.cookie.toString().substring(10)
autenticazione = autenticazione.substring(0, autenticazione.length - 3)
console.log(autenticazione)
jwt.verify(autenticazione, app.get('superSegreto'), function(err) {
if (err) {
res.send('authentication failed!')
} else {
// if authentication works!
next() } })
} else {
console.log('errore')} })
And this is the code for my protected url:
app.get('/miao', function (req, res) {
res.sendFile(__dirname + '/pubblica/inserisciutente.html')
res.end() })
Even though the path is correct (I even tried with path.join(__dirname + '/pubblica/inserisciutente.html) and got the same result), when visiting the url I just get a blank page (with even node conde inside) I also set: app.use(express.static('/pubblica')) P.S. if I try to replace res.sendFile(..) with res.send('Some stuff') I can correctly view it on the page. What am I doing wrong?
res.sendFile() is asynchronous and it will end its own response if it is successful.
So, when you call res.end() right after you start res.sendFile() you are ending the response before the code has actually sent the file.
You can do it like this:
app.get('/miao', function (req, res) {
res.sendFile(__dirname + '/pubblica/inserisciutente.html', function(err) {
if (err) {
res.status(err.status).end();
}
});
});
See the Express doc for res.sendFile() here.
if you want to end the response with res.end() then you must not mention or specify it after res.sendFile() because res.sendFile() is an asynchronous function that means it will take some time to execute and in that meantime next instruction which is in your case is res.end() will execute and that's why you didn't see any response send by the res.sendFile
You can visit the documentation to know more about res.sendFile() visit documentation
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', {});
});