I want to send a message variable that says something like succeeded or failed wihtout the use of passport or sessions.
When it fails I can just do this:
res.status(401).render('register', {
message: 'Your username/email or password is incorrect.'
})
So I was thinking this was possible:
res.redirect('/');
res.render('', { message: 'Login success.'});
But it turns out it isn't:
Error: Cannot set headers after they are sent to the client
Are there any simple sollutions?
I tried making a router.get('/message/redirect :route') but I realised I ran into the same problem there too.
You are redirecting before rendering. So the render method can't send headers because you have already redirected them. You need to call either redirect or render, not both at once. you can do it like this.
res.render('/', { message: 'Login success.'});
remove this line
res.redirect('/');
In your EJS file just call <%= message %> to access the message. There is a better way of dealing with confirmation/error messages. Use Flash.
npm install express-flash
Then before rendering, call the flash. Flash takes two arguments, a name of the message and the message itself, Like this
req.flash("MessageName", "Login success");
res.redirect(pathName);
Make sure to use flash
const flash = require('express-flash');
app = express();
app.use(flash());
app.use((req, res, next) => {
res.locals.MessageName= req.flash("MessageName");
next();
});
To access the message in EJS file just call <%= MessageName %>
Here is sample from my code:
In the server file:
req.flash("error", "Something went wrong");
res.redirect('back');
In the EJS file:
<div class="flash">
<% if(error && error.length > 0){ %>
<div class="flash_error">
<h1 class="flash_error--text"><%= error %></h1>
</div>
<% } %>
</div>
Read this for more information
I coded a simple blog with node.js and used passport for auth and ejs for view engine
when i login some navbar link change as expected but odd thing is when i login from normal chrome tab, and refresh site from another chrome incognito window or even another browser for example edge, I see Logged in version of navbar and i did not login from edge or incognito in chrome
here are my code
html
<% if (!logedIn) { %>
<li>Login</li>
<li>Register</li>
<% } else { %>
<li>New!</li>
<li>LogOut</li>
<li>ChatRoom</li>
<% } %>
server
app.get('/',function(req,res) {
if (req.isAuthenticated()) {
logedIn = true;
}
Post.find(function(err,posts) {
if(err){
console.log(err);;
} else {
res.render('home',{posts:posts,logedIn:logedIn})
}
})
})
Your code is using a global variable logedIn, so if one user is logged in, it treats all other users as logged in as well.
To fix this, simply make logedIn a local variable:
app.get('/',function(req,res) {
// define variables using keywords like var, let, const, etc. Omitting them may create unwanted global variables.
let logedIn = false;
if (req.isAuthenticated()) {
logedIn = true;
}
Post.find(function(err,posts) {
if(err){
console.log(err);;
} else {
res.render('home',{posts:posts,logedIn:logedIn})
}
})
})
I am using EJS and Express to take and check if the current user exists and if not show one thing instead of the other. My issue is that I am setting req.user to res.locals.currentUser but it is not passing through to the template so I can output it as well as check if it exists.
in my app.js
app.use(function(req, res, next){
res.locals.currentUser = req.user;
next();
});
my ejs file
<% if(!currentUser){ %>
<li>Login</li>
<li>Sign Up</li>
<% } else { %>
<li>Signed In As <%= currentUser.email %></li>
<li>Profile</li>
<li>Logout</li>
<% } %>
This evaluates always to false therefore showing the first section of the if statement.
What is wrong here to make it so I can't test or see the user data in my ejs view? When I console.log the currentUser is returns undefined.
Some information that might be important, I am using a redis session store instead of mongo store. I also am using body-parser not cookieparser.
I was facing the same issue while using Passport in node.js with ejs templating.
The solution is to place it below the passport lines of code( that is serialize and deserialize).
These functions are responsible for saving the user to "req.user", and thus if the app.use is placed above it, the code won't work as req.user is null until this.
Adios
I would like to keep session across all the page. For this project, I am using expressJs, nodeJS as server side. AngularJS in front end.
I am not sure, how to handle session when view changes or url changes. Because I need to take care of both expressJS router or angularJs router.
What approach should I follow?
angularJS router
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/welcome', {templateUrl: 'partials/welcome.html', controller: 'MyCtrl2'});
$routeProvider.when('/login', {templateUrl: 'partials/login.html', controller: 'MyCtrl2'});
$routeProvider.when('/signup', {templateUrl: 'partials/signup.html', controller: 'singupController'});
$routeProvider.otherwise({redirectTo: '/'});
}]);
Signup controller
myApp.controller('singupController',function($scope,$rootScope,$http){
$scope.doSingnup = function() {
var formData = {
'username' : this.username,
'password' : this.password,
'email' : null
};
var jdata = JSON.stringify(formData);
$http({method:'POST',url:'/signup',data:jdata})
.success(function(data,status,headers,config){
console.log(data);
}).
error(function(data,status,headers,config){
console.log(data)
});
}
})
ExpressJS router
module.exports = exports = function(app, db) {
var sessionHandler = new SessionHandler(db);
var contentHandler = new ContentHandler(db);
// Middleware to see if a user is logged in
app.use(sessionHandler.isLoggedInMiddleware);
app.get('/', contentHandler.displayMainPage);
app.post('/login', sessionHandler.handleLoginRequest);
app.get('/logout', sessionHandler.displayLogoutPage);
app.get("/welcome", sessionHandler.displayWelcomePage);
app.post('/signup', sessionHandler.handleSignup);
app.get('*', contentHandler.displayMainPage);
// Error handling middleware
app.use(ErrorHandler);
}
After signup, I would like to redirect to the login page. How can I do that in the above router. which one of the following should I use to change the view of app
1) $location of angularJS
2) redirect of ExpressJS
So i had the same problem and to be fair i might have read the approach somewhere i don't remember anymore.
Problem: Angular builds single page apps. After refresh, you loose scope and with it the authenticated user.
Approach
AngularJS modules offer a startup function called run which is called always when the page is loaded. Perfect for refresh/reload.
myApp.run(function ($rootScope, $location, myFactory) {
$http.get('/confirm-login')
.success(function (user) {
if (user && user.userId) {
$rootScope.user = user;
}
});
}
express-session saves the sessions for you and authenticates you with the sessionId your browser sends. So it always knows if you are authenticated or not.
router.get('/confirm-login', function (req, res) {
res.send(req.user)
}
);
All i had to do is, after refreshing and all dependencies were loaded, ask if i am authenticated and set $rootScope.user = authenticatedUserFromExpress;
There are two different concepts here - server side session state and the user state on the client side in Angular. In express you can use the session via req.session to manage session based data.
On the angular side, there is only scope in your controllers. If you want to keep track of some data across multiple controllers, you need to create a service to store the data in and inject the service into the controllers you need.
A typical lifecycle is to first check if there is data already in the service, if so use it. If not, wait for the data to be populated (by the user or app or whatever) then detect those changes and synchronize with your service.
signup controller
function SignupCtrl($scope, $http, $location) {
$scope.form = {}; // to capture data in form
$scope.errorMessage = ''; // to display error msg if have any
$scope.submitPost = function() { // this is to submit your form can't do on
//traditional way because it against angularjs SPA
$http.post('/signup', $scope.form).
success(function(data) { // if success then redirect to "/" status code 200
$location.path('/');
}).error(function(err) { // if error display error message status code 400
// the form can't be submitted until get the status code 200
$scope.errorMessage = err;
});
};
}
sessionHandler.handleSignup
this.handleSignup = function(req, res, next) {
"use strict";
// if you have a validate function pass the data from your
// Signup controller to the function in my case is validateSignup
// req.body is what you need
validateSignup(req.body, function(error, data) {
if(error) {
res.send(400, error.message); // if error send error message to angularjs
}else {
// do something else
// rmb to res.send(200)
}
});
}
validatesignup
function validateSignup(data,callback) {
"use strict"; // the data is req.body
//so now you can access your data on your form
// e.g you have 2 fields name="password" and name="confirmPassword on your form"
var pass = data.password,
comPass = data.confirmPassword;
if(pass != comPass){
callback(new Error('Password must match'), null);
// then show the error msg on the form by using
//angular ng-if like <div ng-if="errorMessage">{{errorMessage}}</div>
}else{
callback(null, data);
}
}
hope this help
Of all the answers here, I like #alknows's approach best. However, like the other answers that suggest you send a request to the server to get the current user data, there are a couple issues I take with them:
You have to deal with race conditions as a result of your AJAX ($http) call.
You're sending an unnecessary request to the server after it already rendered your index.html
I tried #alknow's approach and it worked out for me after I was able to resolve the many race conditions that came up as a result of my angular app controllers and config needing the current user to do their job. I try my best to avoid race conditions when appropriate, so I was a bit reluctant to continue with this approach. So I thought of a better approach: send the current user data down with your index.html and store it locally.
My Approach: Embed currentUser in index.html & store locally on client
In index.html on your server, make a script tag to hold whatever data you want to pass to the client:
```
<!--YOUR OTHER index.html stuff go above here-->
<script id="server-side-rendered-client-data" type="text/javascript">
var __ssr__CData = {
currentUser: { id: '12345', username: 'coolguy', etc: 'etc.' }
}
</script>
```
Then, as #alknows suggested, in app.js or wherever you initiate your angular app, add app.run(..., () => {...}). In app.run(), you will want to grab the server side rendered client data object, which I named obscurely __ssr_CData so that I am less likely to run into name collisions across the global namespace later in my other javascript:
var myAngularApp = angular.module("mainApp", ['ngRoute']);
myAngularApp.run(function ($rootScope) {
const currentUserFromServer = __ssr__CData.currentUser
const currentUserAccessTokenFromServer = __ssr__CData.accessToken
const currentUser =
CurrentUser.set(currentUserAccessTokenFromServer, currentUserFromServer)
$rootScope.currentUser = currentUser
});
As you know app.run() will be called whenever the page does a full reload. CurrentUser is a global class for managing my angular app's current user in the single page environment. So when I call CurrentUser.set(...) it stores the current user data in a place I can retrieve later in my angular app by calling CurrentUser.get(). So in any of your angular app controller's you can now retrieve the current user the server provided by simply doing this:
myAngularApp.controller('loginController',function($scope, $rootScope, $http){
//check if the user is already logged in:
var currentUser = CurrentUser.get()
if(currentUser) {
alert("HEY! You're already logged in as " +currentUser.username)
return $window.location.href = "/";
}
//there is no current user, so let user log in
//...
}
In that example, I made use of CurrentUser.get(), which I explained above, to get the previously stored current user from the server. I could have also retrieved that current user by accessing $rootScope.currentUser because I stored it there, too. It's up to you.
myAngularApp.controller('signupController',function($scope, $rootScope, $http){
//check if the user is already logged in:
var currentUser = CurrentUser.get()
if(currentUser) {
alert("HEY! You're already logged in as " +currentUser.username)
return $window.location.href = "/";
}
//there is no current user, so let user signup
//... you run your signup code after getting form data
$http({method:'POST',url:'/signup',data:jdata})
.success(function(data,status,headers,config){
//signup succeeded!
//set the current user locally just like in app.js
CurrentUser.set(data.newUser)
//send user to profile
return $window.location.href = "/profile";
})
.error(function(data,status,headers,config){
//something went wrong
console.log(data)
});
}
Now, after a new user has signed up, your server returned the new user from the AJAX call. We set that new user as the current user by calling CurrentUser.set(...) and send the user to their profile. You can now get the current user in the profile controller the same way you did to check if the current user existed in the login and signup controllers.
I hope this helps anyone who comes across this. For your reference, I'm using the client-sessions module to handle sessions on my server.
I currently have an express/node application I want to test, and but responses that it gives are in the form of req.flash('warn', 'message goes here');
Unfortunately, the documentation on express.js does not describe how this message travels to the client very thoroughly.
I know expresso has an assert.response() function that tests response objects. I was wondering where the flash message goes, and how I can test it in a similar way (or if it's not possible, and I should be sending everything through the response object).
i don't think this is supposed to work like that. you can't just use req.flash()as your only way to respond to a request.
it's more like an easy way to flash messages to the user on your normal templates - e.g. after inserting/creating an article you can either say:
req.flash('error', 'could not insert because .... ');
or
req.flash('info', 'article added successfully!');
for my last project i then added two dynamic helpers to my app:
app.dynamicHelpers({
info: function (req, res) {
return req.flash('info');
},
error: function (req, res) {
return req.flash('error');
}
});
so that i can just say sth like this in my view:
<% if (info !== undefined && info != "") { %>
<div class="infoBubble">
<%= info %>
</div>
<% } %>
<% if (error !== undefined && error != "") { %>
<div class="errorBubble">
<strong>Fehler</strong>: <%= error %>
</div>
<% } %>
result looks like this: