I'm trying to redirect on successful registration. I've knocked together this basic code to test out the principle:
app.post('/register', function(req, res, next) {
User.findOne({ username: req.body.username }, function (err, user) {
if (err) { return done(err); }
if (user) {
res.send(400);
} else {
var newUser = new User({ username: req.body.username, password: passwordHash.generate(req.body.password)});
newUser.save();
req.logIn(newUser, function(err) {
if (err) return next(err);
return res.redirect('/my-events');
})
}
});
});
However, when this triggers, my browser sees a 302, followed by a successful GET of the page that the /my-events endpoint serves up...but no redirect.
What's wrong? Using res.redirect works when the browser is loading an endpoint, it just doesn't seem to work as the result of a $.post.
You mentioned that you are using $.post for sending the data. If that is the case then server won't be able to redirect to the actual page. What you need to do is redirect the page manually yourself in the call back function of the post function like so.
$.post('your endpoint',function(){
window.location = 'page to redirect to';
});
This should do it.
Related
This is my register route. If the registration is successful I want to redirect to the secret route. But the response is not working. When it is successful the page keeps loading and loading and doesn't redirect me to the secret route.
app.post('/register', function(req, res) {
User.create({
username: req.body.username,
password: req.body.password,
email: req.body.email
}, function(err, result, res) { **HERE**
if(err) throw err;
res.redirect('/secret');
console.log(result);
});
});
I tried this and the /secret route works but when I do this it doesn't check for registration errors and immediately redirects to the secret route.
app.post('/register', function(req, res) {
User.create({
username: req.body.username,
password: req.body.password,
email: req.body.email
}, function(err, result) { **HERE**
if(err) throw err;
console.log(result);
});
res.redirect('/secret');
});
I tried to add return err. So if there is an error the function will exit.
But here the /secret route still is still shown when I intentionally made an error in the registration. So the return is not exiting the function.
app.post('/register', function(req, res) {
User.create({
username: req.body.username,
password: req.body.password,
email: req.body.email
}, function(err, result) {
if(err) return err; **TRIED RETURN TO EXIT**
});
res.redirect('/secret');
});
That it keeps loading and loading indicates that the create user function returns an error in the callback. Or an error occured in the callback.
In case of an error no reponse might be send which will result in a dangling request.
In your code the reason is that you have to remove the res from the create user callback otherwise you will hide the original res of the middelware callback, which will result in an error. But you also have to handle the case when create user itself results in an error.
Your callback could look that way:
function(err, result) {
if(err) {
res.status(500).send('an error occured')
} else {
res.redirect('/secret');
console.log(result)
}
}
How exaclty the callback should look like depends on what you want to do in case of the error. You also might want to choose a different error code then 500 that would match better.
I believe you're confusing the callback-based nature of asynchronous programming in normal NodeJS with regular sequential programming.
The call to User.create will return immediately, and lines following it execute prior to anything within the callback you provide. So your res.redirect() call will trigger the immediate response of a redirect to the client. When the callback finally runs, the request has finished.
Instead, you will want to perform the redirection inside of the callback function, after checking for potential errors.
Your code should end up looking like this:
app.post('/register', function(req, res) {
User.create({
username: req.body.username,
password: req.body.password,
email: req.body.email
}, function(err, result) {
if(err) throw err;
// Since we got here, things went well presumably
res.redirect('/secret');
});
});
I'm using NodeJS and passport to let users create an account before they can see results of a quiz they've just taken. My challenge is I need to confirm the username is available before the page refreshes because the user will lose their results if this happens.
Again: I need to verify the username is not taken prior to refreshing.
I think I'm close but it is not working. How would I change my code to handle this challenge?
Currently if the user name is taken it returns an error on trying to create an account and the user ends up on the /failpage as shown below.
app.post('/quiz', usernameToLowerCase, emailToLowerCase, function(req, res) {
User.findOne({
username: req.body.username
}, function(err, user) {
if (err) {
alert(err)
if (user) {
alert('this username is already taken. Please choose another.')
console.log('there was a user');
return false;
}
}
});
var user = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password,
})
user.save(function(err) {
console.log('this is the problem' + ' ' + err)
if (err) {
return res.redirect('/failpage')
}
req.logIn(user, function(err) {
if (err) {
console.log(err);
}
console.log('all looks good')
res.redirect('/results');
});
});
});
Solved it with this if anyone else is trying to do the same thing:
in app.js
app.get('/usercheck', function(req, res) {
User.findOne({username: req.query.username}, function(err, user){
if(err) {
console.log(err);
}
var message;
if(user) {
console.log(user)
message = "user exists";
console.log(message)
} else {
message= "user doesn't exist";
console.log(message)
}
res.json({message: message});
});
});
In js
$('#usercheck').on('change', function() {
$.get('/usercheck?username='+$('#usernameValue').val().toLowerCase(), function(response) {
$('#usernameResponseHidden').text(response.message)
if ($('#usernameResponseHidden').html() === "user exists"){
$('#usernameResponse').text('That username is taken. Please pick another')
}
To solve your problem I think you need to routes. At least a app.get('/quiz') which returns a boolean on if the user exists or not. The section User.findOne can be sent in that route instead. You just need to make a request using ajax when he looses focus of the username field of your form, and display a notification if the name is available or not.
New to nodejs, liking it so far, trying to do some custom handling for passport authentication. The success redirect works fine, however upon an unsuccessful attempt i would like to not continue the post event.
What I'm trying to accomplish is fire an alert off that opens a dialog (this part is working like I want, via the socket call) to the current page if there is an issue with the login attempt.
The browser just waits if I don't call res.send() for example, or attempts to redirect to a page that does not exist if I do.
routes.js
app.post('/login', function (req, res, next) {
passport.authenticate('local-login', function (err, user, msg) {
if (err) {
io.emit('status alert', err);
}
if (!user) {
io.emit('status alert', msg);
//res.send();
}
req.logIn(user, function (err) {
if (err) {
io.emit('status alert', err);
}
if (user) {
return res.redirect('/loginsplash');
}
});
})(req, res, next);
});
passport.js
passport.use(
'local-login',
new LocalStrategy({
usernameField : 'username',
passwordField : 'password',
passReqToCallback : true
},
function (req, username, password, done) {
db.getConnection().query("SELECT * FROM users WHERE uname = ?", [username], function (err, rows) {
if (err)
return done(err);
if (!rows.length) {
return done(null, false, 'Invalid Username or Password.');
}
// if the user is found but the password is wrong
if (!bcrypt.compareSync(password, rows[0].upw))
return done(null, false, 'Invalid Username or Password.');
// all is well, return successful user
return done(null, rows[0]);
});
})
);
Well after a weekend of persistence and trial/error, my hunch to use ajax seems to be right. I finally came across a similar so here that was helpful. Hope this helps someone else, I'm probably going to tweak it some, but right now basically if the ajax response is empty (res.end), my custom dialog pops up. If there's something in it (from the res.redirect), it redirects to the intended page :
passport.js, no changes from above
routes.js
app.post('/login/ajax', function (req, res, next) {
passport.authenticate('local-login', function (err, user, info) {
if (err) { return next(err); }
if (!user) { return res.end(); }
req.logIn(user, function (err) {
if (err) { return next(err); }
return res.redirect('/loggedin');
});
})(req, res, next);
});
ajax call in .ejs page
$.ajax({
type: "POST",
url: "/login/ajax",
data: { username: username , password: password },
dataType: 'html',
success: function(data) {
if (data) {
window.location.href = '/loginsplash';
}
else {
OpenFeedbackMsgDlg("Login Failed");
}
}
}).done(function () {
console.log("http request succeeded");
alert("login success");
});
});
If anyone has suggestions/alternate approaches, I'd still be interested in learning.
I check whether or not an email or username is taken. I then use then use flash to send a message to the client using req.flash('messages', 'That username is taken.'); The only problem is I can't call a request command within a function. I can send the message when I put it right after app.post('/signup', function(req, res) { How could I check if a username is taken and then send the req.flash command once it has been determined. I tried to create an if statement but because node is run asynchronously by the time the username is found and the variable is set to true, the if statement has been called. How could I use req.flash to send a message to the client within this post request.
app.post('/signup', function(req, res) {
var userDetails = User({
firstname: req.body.firstname,
username: req.body.username,
email: req.body.email,
password: bcrypt.hashSync(req.body.password1, bcrypt.genSaltSync(10))
});
User.findOne({
$or: [ { 'username': req.body.username}, {'email': req.body.email}]
}, function(err, user) {
if (user) {
if(user.username === req.body.username){
console.log('that username is taken');
req.flash('messages', 'that username is taken');
} else {
}
if(user.email === req.body.email){
console.log('that email is already in use');
req.flash('messages', 'that email is already in use');
} else {
}
} else {
userDetails.save(function(err) {
if (err) throw err;
});
console.log('change to login')
}
if (err) {
return done(err);
}
});
res.redirect('/');
});
It should be no problem to call req in a other function if req is defined in a higher level. I am not sure if your flash is session stored, if it not, the reasen why the messages is not flash is your redirection to route.
You redirect to root without waiting to the end of database request. I think you will only redirect if user is found? Include your redirect to the callback function.
I'm working on an app which has node.js and express on the server, mongodb for the db and Backbone.js on the front-end. I'm working on enabling user logins, so I've used the passport.js library. I have a problem with my login 'post' method: It is not redirecting to another page (well it's an SPA, so I mean rendering a Backbone view). Here's the code:
//standard express setup...
app.post('/api/auth', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
req.session.messages = [info.message];
return res.redirect('/')
}
req.logIn(user, function(err) {
if (err) {
return next(err);
} else {
console.log('yup, working'); //Can see the response in the console
return res.redirect('/api');
}
});
})(req, res, next);
});
app.get('/api', function (request, response) {
response.send( 'Login successful!' );
});
So I can see the console.log message fine, and a GET request for the route IS triggered...but nothing actually happens. So I'm thinking that I've misunderstood how 'res.redirect' works - I want to navigate to that route upon the login success. I've thought about using window.location, but is this a good long-term solution? I'm not using any html templates on the server, so I can't (I don't think) do something as simple as 'res.render('index')'
What would be the best way to approach this? Thanks in advance.
I had a face-palm moment a few days after asking this question where I realized I had failed to grasp a core concept correctly. Let me answer my own question:
Rather than trying to serve the page from the server, I just need to send a response to the client and have Backbone do the rendering, depending on the response received. So my server does something more like this:
controller.post('/user', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
user.save(function(err) {
if(err){
console.log(err);
return res.json(401);
} else {
return res.json(200); //SEND RESPONSE - do not try to redirect
}
});
Then I do the page loading in the Backbone View, like this:
login: function (e) {
e.preventDefault();
console.log('submitting login request');
var formValues = {
username: $('#inputEmail').val(),
password: $('#inputPassword').val()
};
var user = new User(); //user model
user.set(formValues);
function saveUserfunction (){
if(user) {
user.save(this.formValues, {
success: function(model, response, options) {
if (response == 200) {
console.log('success :' + response);
window.location.href = 'http://localhost:4711/#/api/menu_auth'; //Here's my redirect - the router is listening for this route and will render accordingly
} else {
console.log('error: '+response);
}
}, error: //Error handling etc.
As Pheonix pointed out in the comments, the best way to do this would be to listen to the the user model.