I am currently working on a project that uses MongoDB, Express, and Jade. We always want to throw the user account JSON data into the view. Every single controller we have, we are writing
exports.theView = function(req, res){
User.findOne({ username: req.user.username }, function(err, user){
res.render('theview.jade', { user: user });
});
}
There seems like there should be a better, more efficient way of doing this.
Any suggestions?
I think you can use middleware to set response locals (http://expressjs.com/api.html#res.locals) to set user to view every reqeust.
Middleware:
app.use(function(req, res, next) {
User.findOne({ username: req.user.username }, function(err, user){
res.locals.user = req.user;
next()
});
});
Route:
app.get('/', function(req, res, next) {
res.render('theview.jade', {title: 'Title'});
});
View:
block content
h1= title
p= user.name
Related
I am using express and I want to have my user profile URLs like this: example.com/:username
However, I still need other URLs such as example.com/login and example.com/view/:id
If I order the router like this, it treats "login" as a username when a request is sent to example.com/login:
router.get('/:username', function (req, res, next) {
res.render('profile', {data: req.params.username});
})
router.get('/login', function (req, res, next) {
res.render('login', {data: null});
})
router.get('/view/:id', function (req, res, next) {
res.render('view', {data: req.params.id});
})
If I put the /:username router at the end, everything works correctly. However, if someone went to example.com/view (without an id), I need it to send an error that the view controller didn't receive an id. Instead, it sees it as a username again and instead sends an error that the username doesn't exist.
What is the cleanest way to solve this? Do I just have to add a router for all base url paths? (Something like this):
router.get('/login', function (req, res, next) {
res.render('login', {data: null});
})
router.get('/view/:id', function (req, res, next) {
res.render('view', {data: req.params.id});
})
router.get('/view', function (req, res, next) {
res.render('viewError', {data: null});
})
router.get('/:username', function (req, res, next) {
res.render('profile', {data: req.params.username});
})
I am not entirely sure if this is the right way to do it, but then again this sounds like something I might encounter and I would like it to be solved by this method until I find a better solution.
The below solution uses a single route for the path format /:value, be it login, view or any username, hence you could put in a simple if-else-if or switch to give control to respective controllers or just simply render a view from it. This way the order in which it has to be handled doesn't matter at all.
router.get("/:username", function(req, res, next) {
if (req.params.username === "login") {
res.render("login", { data: null });
} else if (req.params.username === "view") {
res.render("viewError", { data: null });
} else {
res.render("profile", { data: req.params.username });
}
});
router.get("/view/:id", function(req, res, next) {
res.render("view", { data: req.params.id });
});
I am using MEAN Stack and mongoose. Using passport for authentication. I want to get the currently logged in user and display his details. For example if I am logged in and if I go to the profile page I want to get my details. There were only few resources on the internet.
Login.js (passport and routes are here)
var express = require('express');
var router = express.Router();
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/user');
var ConnectRoles = require('connect-roles');
router.get('/', function(req, res, next) {
res.render('Login', { title: 'Login' });
});
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use("/", new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false,
{ message: "No user has that username!" });
}
user.checkPassword(password, function(err, isMatch) {
if (err) { return done(err); }
if (isMatch) {
return done(null, user);
} else {
return done(null, false,
{ message: "Invalid password." });
}
});
});
}));
router.use(function(req, res, next) {
res.locals.currentUser = req.user;
res.locals.errors = req.flash("error");
res.locals.infos = req.flash("info");
next();
});
router.post("/", passport.authenticate("/", {
successRedirect: "/SubmitSurvey",
failureRedirect: "/",
session: false,
failureFlash: true
}));
router.get("/AdminDatabase", function(req, res) {
res.render('AdminDatabase', { title: 'AdminDatabase' });
});
router.get('/CreateSurvey', function(req, res, next) {
res.render('CreateSurvey', { title: 'CreateSurvey' });
});
router.get('/SubmitSurvey', function(req, res, next) {
res.render('SubmitSurvey', { title: 'SubmitSurvey' });
});
router.get('/profile', function(req, res, next) {
res.render('profile', { title: 'profile' });
});
router.get("/Logout", function(req, res) {
req.logout();
res.redirect("/");
});
module.exports = router;
How can I do this in nodejs
It's on the req.user object
router.get('/profile', function(req, res, next) {
//here it is
var user = req.user;
//you probably also want to pass this to your view
res.render('profile', { title: 'profile', user: user });
});
passport fills the req.user object with the current user through middlewares pipelines so you can extract it from there.
As Alex said, you may explicitly pass some variable (e.g. your req.user) when calling res.render:
router.get('/profile', function(req, res, next) {
res.render('profile', { title: 'profile', user: req.user });
});
Instead, maybe you'd like some variables (like that req.user) to be accessible in all calls to res.render without bothering to specify them every time? In that case, you can use res.locals, e.g. by adding this middleware:
app.use(function(req, res, next){
res.locals.user = req.user;
next();
});
And finally, for completeness -- in both of the approaches above, the variable user is available for you in the view. For example, if you're using a pug template, you may now do something like:
if user
p Hello, #{user.id}, good to see you!
else
p Please log in...
I m new in NODE js. so just need to know can we check session in one place rather than applying checks at each method.
This is possible to use single check at server.js for session management.
Thanks in advance
Please make use of express middleware
app.use( function(req, res, next) {
// req.session #use this object
next();
});
make it into a global middleware function
app.use(function(req, res, next) {
if (req.session && req.session.user) {
User.findOne({ email: req.session.user.email }, function(err, user) {
if (user) {
req.user = user;
delete req.user.password;
req.session.user = user;
res.locals.user = user;
}
next();
});
} else {
next();
}
});
I set up a navbar.ejs as a partial so that i don't need to rewrite it again. Every routes require navbar.ejs. Im using passport for Auth
<% if (!user) { %>
<li>Signup</li>
<li>Login</li>
</ul>
<% } else{ %>
<li>Dashboard</li>
<li><a><%= user.profile.name %></a></li>
<% } %>
Example routes
app.get('/', function(req, res) {
res.render('home');
}
app.get('/dashboard', isLoggedIn, function(req, res) {
res.render('dashboard', { user: req.user });
}
app.get('/about', function(req, res) {
res.render('about');
}
For routes that have user's object as the response are working fine when rendered but others will keep showing
user is not defined
I know that the obvious solution is to pass in a user's object in every single route, but that is really insane.
Imagine if I have to do this
app.get('/1', function(req, res) {
res.render('/1', {user: req.user});
}
app.get('/2', function(req, res) {
res.render('/2', {user: req.user});
}
app.get('/3', function(req, res) {
res.render('/3', {user: req.user});
}
app.get('/4', function(req, res) {
res.render('/4', {user: req.user});
}
app.get('/5', function(req, res) {
res.render('/5', {user: req.user});
}
If routes that require authentication, I don't really mind to pass the user's object, but simple routes like home, about or contact I don't feel like the need to pass in.
If you don't want to keep passing the user's object in every single route then you have to set up a middleware
somewhere after your passport configuration add the following codes
app.use(function(req, res, next){
res.locals.user = req.user;
});
then you no longer needed to pass the user's object in every single route
app.get('/1', function(req, res) {
res.render('/1');
}
app.get('/2', function(req, res) {
res.render('/2');
}
I have the following working code to authenticate through the passport-local strategy:
app.post('/api/login', passport.authenticate('local-login', {
successRedirect : '/api/login/success',
failureRedirect : '/api/login/error',
failureFlash : true
}));
app.get('/api/login/error', function(req, res) {
res.send(401, {error: req.flash('loginMessage')});
});
app.get('/api/login/success', function(req, res) {
res.send(200, {user: req.user});
});
However, ideally I want to handle the errors and success messages from one express route, and not redirect to two extra routes.
Is this possible? I tried using a 'custom callback' but that seemed to error out on serializing users for some reason.
You can use custom callback, such as:
passport.authenticate('local', function (err, account) {
req.logIn(account, function() {
res.status(err ? 500 : 200).send(err ? err : account);
});
})(this.req, this.res, this.next);
In err object you can find all needed errors, which was appeared at authentication.
Are you using Mongoose?
Try adding this to your server.js/index.js
var User = mongoose.model('User');
passport.use(new LocalStrategy(User.authenticate()));
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
This to your routes index.js
var auth = require('./auth');
app.post('/api/auth/login', passport.authenticate('local'),auth.login);
auth.js:
var UserModel = require('../models/user');
var User = new UserModel();
exports.login = function(req, res) {
var user = req.user;
req.login(user, function(err) {
//if error: do something
return res.status(200).json(user)
});
};
Add this to model index.js
var passportLocalMongoose = require('passport-local-mongoose');
userSchema.plugin(passportLocalMongoose, {
usernameField: 'email',
usernameLowerCase: 'true'
});
I'm making a lot of assumptions on structure and packages here. but this should work
EDIT
For custom callbacks:
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
Here you instead of res.redirect you can use something like return res.status(404).json("Not Found)
See docs for more information : http://passportjs.org/guide/authenticate/