Add flash message to form without losing page - node.js

I am using node.js and express to make a CRUD web app, and I am trying to work out how to get a flash message to appear on a data entry form when server side validation fails. I am using express-flash for the flash messages.
The validation code works, returning errors, and I create the flash message:
var errors = validateForm(req);
if(errors){
req.flash('info',errors);
res.render('edit', {messages: req.flash('info')});
}
And display the message in the edit.jade file:
if messages.info
p #{messages.info}
The problem is that I am editing a specific object, and the url is not /edit, but /edit/objectID. The get for the page is like this:
router.get('/edit/:id', function(req, res) {
var db = req.db;
var collection = db.get('mydb');
collection.find({ID: req.params.id},{},function(e,docs){
res.render('edit', {"object" : docs});
});
});
Is it possible to add a flash message for server side validation after a POST has been sent without losing the page ID? I don't need all the data from the edit, there will be client side validation to hopefully pick up any errors, but I would like server side validation errors to cause the user to land on the same object page as they left.

You can declare your post route as:
router.post('/edit/:id', function(req, res) {
// logic to check for errors
// if errors, set flash message and
// redirect to /edit/:id
});
Then on, you can use the ID in the post route to redirect to the edit page of the same resource that the user posted the form for. You will also be able to access the flash error messages in the template.
* Edit *
In order to use flash on redirect you would need to add it the middleware.
app.configure(function() {
app.use(express.cookieParser('keyboard cat'));
app.use(express.session({ cookie: { maxAge: 60000 }}));
app.use(flash());
});

Related

node express routing decision based on user-agent

Trying to figure out a way of supplying better data to social media (open graph data). Basically, when facebook, twitter or pinetrest asks for information about a link on my page, I want to provide them og information dependent on link instead of sending them the empty page (OK, it sends javascripts that they dont run).
I tried using prerender and similar, but cant get that to run propperly. But I also realised that I would rather get the express router to identify it and service a static page based on the request.
As a first step, I need to get the user agent information:
So I thought I would add express-useragent, and that seems to work on my test site, but does not seem like facebooks scraper ever goes past it. I can see it tries to get a picture, but never updates the OG or the index. (code below should work as an example)
var express = require('express');
var router = express.Router();
var useragent = require('express-useragent');
//Set up log
var cfgBunyan = require('../config/bunyan')
var log = cfgBunyan.dbLogger('ROUTE')
router.use(useragent.express());
/* GET home page. */
router.get('/', function(req, res, next) {
console.log(req.useragent);
res.render('index');
});
router.get('/share/:service', function(req, res, next) {
res.render('index');
});
router.get('/pages/:name', function (req,res, next){
log.info('/pages/'+req.params.name)
res.render('pages/'+req.params.name);
});
router.get('/modals/:name', function (req,res, next){
res.render('modals/'+req.params.name);
});
router.get('/page/:name', function (req,res, next){
res.render('index');
});
module.exports = router;
I can also tun the google test scraper, which gives me the following source
source: 'Mozilla/5.0 (compatible; Google-Structured-Data-Testing-Tool +https://search.google.com/structured-data/testing-tool)' }
So has anyone figured out a easy way to direct facebook and twitter to another route? Or is sitting and checking the different sources the right way?
OK, so I managed to figure out a potential solution.
Basically, I created a function called isBot, which I call similar to how Authentication works, it will send the request to isBot, and check if.
1. ?_escaped_fragment_= is pressent in the url (Google and some others use that)
2. if the user agent is a known bot (Thanks prerender.io, borrowed your list from .htaccess for your service)
The setup is simple enough.
Add (You don't have to, Rob was right) express-useragent to your router (just to be able to get info from the header)
//var useragent = require('express-useragent'); //Not needed ror used
//router.use(useragent.express()); // Thought this was required, it is not
Then in any route you want to check for bots add isBot:
router.get('/', isBot ,function(req, res, next) {
Then add the below function (it does a lot of logging using bunyan, as I want to have statistics, you can remove any line that starts log.info, it should still work, or add bunyan, or just change the lines to console.log. Its just output.
If the code decides the code isn't a bot, it just renders as normal
function isBot (req, res, next){
var isBotTest = false;
var botReq = "";
var botID= ""; //Just so we know why we think it is a bot
var knownBots = ["baiduspider", "facebookexternalhit", "twitterbot", "rogerbot", "linkedinbot","embedly|quora\ link\ preview","howyoubot","outbrain","pinterest","slackbot","vkShare","W3C_Validator"];
log.info({http_user_agent: req.get('User-Agent')});
//log.info({user_source: req.useragent.source}); //For debug, whats the HTTP_USER_AGENT, think this is the same
log.info({request_url: req.url}); //For debug, we want to know if there are any options
/* Lets start with ?_escaped_fragment_=, this seems to be a standard, if we have this is part of the request,
it should be either a search engine or a social media site askign for open graph rich sharing info
*/
var urlRequest=req.url
var pos= urlRequest.search("\\?_escaped_fragment_=")
if (pos != -1) {
botID="ESCAPED_FRAGMENT_REQ";
isBotTest = true; //It says its a bot, so we believe it, lest figure out if it has a request before or after
var reqBits = urlRequest.split("?_escaped_fragment_=")
console.log(reqBits[1].length)
if(reqBits[1].length == 0){ //If 0 length, any request is infront
botReq = reqBits[0];
} else {
botReq = reqBits[1];
}
} else { //OK, so it did not tell us it was a bot request, but maybe it is anyway
var userAgent = req.get('User-Agent');
for (var i in knownBots){
if (userAgent.search(knownBots[i]) != -1){
isBotTest = true;
botReq=urlRequest;
botID=knownBots[i];
}
}
}
if (isBotTest == true) {
log.info({botID: botID, botReq: botReq});
//send something to bots
} else {
log.info("We don't think this is one of those bots any more")
return next();
}
}
Oh, and currently it does not respond to the bot requests. If you want to do that, just add a res.render or res.send at the line that says //send something to bots

Node Express auth status

I have multiple routes, split into different files (my app consists of different "modules", which I maintain in separate folders. For each folder, there is an index.js file in which I manage the routes per module, and I require these in the app.js file).
For every route, I will require to check the auth, and pass the loggedIn status to the header of every page:
//Default variables for the ejs template
var options = {
loggedIn: true
};
res.render("home/home", options);
If the logged in status is true, then the user's name will be displayed. If not, the login / signup labels are displayed.
What is the best way to centralise this, so that I don't need to require the auth script in every of these index.js (route) files?
I need to be able to pass the auth status to the view via the options object (see example).
In your auth, module, use a middleware function. That function can check and store res.locals.loggedIn which will be available for any view that will eventually be rendered. Just make sure the app.use call executes prior to your other routes and it will work properly.
app.use(function auth(req, res, next) {
res.locals.loggedIn = true; // compute proper value here
next();
});
From what I understand you need to do this for every request.One common thing is adding this as middleware so that all the request gets this .
For Example :
var http = require('http');
var connect = require('connect');
var app = connect();
app.use(function(req, res) {
res.end('Hello!');
});
http.createServer(app).listen(3000)
Now for every request , Hello is printed . You could extract this as a module and reuse it across projects. Check here for more details

Why does this flash policy module (express middleware) use _.clone() in Sails.js?

From a tutorial on Sails, this is a Sails policy - essentially Express middleware:
module.exports = function(req, res, next) {
res.locals.flash = {};
if (!req.session.flash) return next();
res.locals.flash = _.clone(req.session.flash)
// clear flash
req.session.flash = {};
next();
};
I'm totally lost:
What's the difference between req.locals.flash and req.session.flash?
Why do the response locals.flash have to be cleared right off the bat?
Why are response locals filled with cloned request session flash?
What's the difference between req.locals.flash and req.session.flash?
req.locals.flash will available in its view, but the req,session
relates with the session of browser.
More info locals vs session
Why do the response locals.flash have to be cleared right off the bat?
Make sure that the error message just display one time only.
Why are response locals filled with cloned request session flash?
Cause if not the locals will keep the pointer of the session. So if
the "req.session.flash = {};", the locals will also be empty
This is a policy, it will be invoked each time you visit a url(depends on your config/policies.js), what it does is to consume the message.
Clearing req.session.flash is to make sure the message is only displayed once, clearly you don't want to be haunted by the message again and again. :D
Hope that helps.

AngularJS and ExpressJS session management?

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.

Not cookie based session management in node.js

I am looking for a non-cookie based session management in node.js, something like pass a parameter in the URL like &session_id=. It will know that a session has expired when a request comes with an session_id. I've looked at connect library, but it looks that it is cookie based only.
Warning
Passing the session id as a GET parameter is considered bad practice. Why? It is dangerous because people don't usually care about session id and they will probably publish/share links with their session ids inside.
It's also a problem because when a user clicks an external link on your web, and goes to another site, that new site will be able to see the session_id in the referrer link.
So I don't think it is a good idea. Cookies are more secure.
Have a look at: Session Hijacking
For every request you receive, you will get all of the client cookies accordingly.
You can also set client cookies in the response HTTP headers using "Set-Cookie."
Using a GET parameter is unsafe. Any user could accidently share their session ID, but if you want 100% security, I'd share session IDs via cookies, and I would use HTTPS to prevent snoopers from stealing cookies.
You can use localstorage or sessionStorage..
almost same as cookie
not a cookie
better than a cookie!
More info: https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Storage
It's very -very- easy to use... in Js for example:
<script>
// check if Storage is avaible
if(typeof(Storage)!=="undefined") {
// Save data to local storage (no exiparion date)
localStorage.setItem("name_always", "bxx");
// Save data to the current session (removes when the tab is closed)
sessionStorage.setItem("name_now", "bxx");
} else {
// No Storage support...
}
// Access to stored data
alert( "For only now, my name is: " + sessionStorage.getItem("name_now"));
alert( "Every day, my name is: " + localStorage.getItem("name_always"));
</script>
Tags: javascript html5 local-storage session-storage
You can use sessions with a store in node.js. For example, you have express application and want to use session like system in your webapp. You can use connect-mongo module for this. This will let you store your session in db. In your app.js
var express = require('express'),
, mongoStore = require('connect-mongo')(express);
var app = express();
app.configure('all', function () {
app.use(express.session({
secret: "terces",
cookie: { maxAge: 24 * 60 * 60 * 1000 },
store: new mongoStore({
url: your_db_url
})
}));
app.use(function(req, res, next) {
res.locals.session = req.session;
next();
});
});
With basic code above, you have session in express that you can use it in your controllers and views directly. In your controller;
app.post('/blog/create/?', function(req, res, next) {
if (!req.session.user) {
next("You need to login in order to create blog!");
}
});
In your view, you can use session.user in order to generate profile menu for example.

Resources