Why does this flash policy module (express middleware) use _.clone() in Sails.js? - node.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.

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

Add flash message to form without losing page

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());
});

nodejs setting object in req.session.data but not getting that data in next request?

i am setting the object in req.session.data,
req.session.data = customObj;
console.log(req.session.data);
it is showing the correct data in req.session.data
But in the next request, when i print session object data by
console.log(req.session.data);
it is showing 'undefined'
I need that data in every next request.
how to resolve it?
It turns out, running
req.session.save(function(err) {
// session saved
});
commits those changes and makes them accessible outside of the current scope - essentially forever, for that req.session.
I had the exact same issue and lucked into this by trial and error, and it should be noted I'm not sure what other functionality might be implied by req.session.save.
I suspect you have not enabled a session store
Where you start your express app, ensure you have app.use(express.session....
eg:
var express = require('express');
var app = express();
app.use(express.cookieParser());
app.use(express.session({secret: '1234567890QWERTY'})); //can be any random value
More information: http://blog.modulus.io/nodejs-and-express-sessions

NodeJS + session object in ALL views without passing it on all controller actions

I want my session to be available in all views (*.ejs) without having to pass it on every single action. My code is shown below, but the req.session object is always null here, even though in my "controllers" I can access a session object after an user has authenticated, by specifying:
req.session.whatever
My initialization code (that is currently executed on every single request (I double checked with a debug breakpoint) is:
var appendLocalsToUseInViews = function(req, res, next)
{
//append request and session to use directly in views and avoid passing around needless stuff
res.locals.request = req;
if(req.session != null && req.session.user != null)
{
res.locals.user = req.session.user;
}
next(null, req, res);
};
I register this function in the app setup preamble:
app.use(appendLocalsToUseInViews);
I have seen people use app.use methods and dynamicHelpers. I am using express 3, and it seems they are gone, deprecated from Express 2... But that does not seem to be the point, as the function is being called correctly on every single request. How to I access the Express session in this sort of pre-controller code?
Thanks!
SOLUTION thanks Jani Hartikainen:
I moved the code to after the session middleware is loaded and its working!!! Here is the new code.
app.use(express.cookieParser(appSecret));
app.use(express.session({ secret: appSecret }));
---->>>app.use(appendLocalsToUseInViews);
This should work but make sure your app.use for this is only after you have initialized your session middleware. If you have this before the initialization for the session middleware, it will be ran before it in the chain, and thus the data will not be available.

Is it OK to add data to the response object in a middleware module in Express.js?

Here's the basic setup. I'm trying to create a simple middleware component that would allow me to easily pass data from my route directly to my javascript in the client side. (Very similiar to the Gon gem in ruby). The way I'm doing it is by having a module that looks like this:
module.exports = function(){
return function(req,res,next){
var app = req.app;
if(typeof(app) == 'undefined'){
var err = new Error("The JShare module requires express");
next(err);
return;
}
res.jshare = {};
app.dynamicHelpers({
includeJShare: function(req,res){
if(typeof(res.jshare) === 'undefined'){
return "";
}
return function(){
return '<script type="text/javascript">window.jshare=' + JSON.stringify(res.jshare) + '</script>';
}
}
});
next();
};
}
Then, in my route I can do this:
exports.index = function(req, res){
res.jshare.person = {firstName : "Alex"};
res.render('index', { title: 'Express' })
};
Finally in the layout.jade:
!{includeJShare()}
What that does is in outputs a line of javascript on the client that creates the exact JSON object that was created server side.
Here's the question; it all works as expected, but being new to Express and Node.js in general, I was just curious if attaching properties onto the response object is OK, or is there something wrong with doing it that I'm simply overlooking? For some reason it doesn't pass my "smell test" but I'm not sure why.....
I know this is an old thread, but there is something else to add to this topic.
Express has a response.locals object which is meant for this purpose - extending the response from middleware to make it available to views.
You could add a property directly to the response object, and as #hasanyasin indicated, is how JavaScript is designed. But Express, more specifically, has a particular way they prefer we do it.
This may be new in express 3.x, not sure. Perhaps it didn't exist when this question was asked.
For details, see
http://expressjs.com/en/api.html#res.locals
There is also an app.locals for objects which don't vary from request to request (or response to response I suppose).
http://expressjs.com/en/api.html#app.locals
See also: req.locals vs. res.locals vs. res.data vs. req.data vs. app.locals in Express middleware
It is perfectly OK. It is how JavaScript is designed. Only thing you should be careful is to not accidentally overriding already existing properties or being overridden by others. To be safer, instead of adding everything directly to req/res objects, you might consider going a level deeper:
res.mydata={}
res.mydata.person= ...
Like that.
Use res.locals for including custom variables in your response object.

Resources