I am quite new to sailsjs and nodejs. I am trying to create an authentication page, wherein once a user is authenticated, I want to set
req.session.user = user.id
req.session.authenticated = true
I then need to access these values within my main layout.ejs file. I have done so by using
res.locals.user = _.clone( req.session.user )
However, I noticed that this clone method has to be called in every function of every controller so as to allow me to be able to access the user from within the views. Is there a better way of accessing session variables in sailsjs globbaly, without having to clone the req.session in every controller. An example of what I am doing can be found here:
http://pastebin.com/HyE2H4Kq
As you can see, I have called the clone method at the beginning of various functions within the controller.
The req object is available in your views by default, unless you overwrite res.locals completely. So you can access a session variable in your view with <%=req.session.user%> (if you're using EJS). You don't have to copy it explicitly into your locals.
for anyone using passport.js, session of the user is saved inside req.session.passport.user and you also can access it directly from the view
I have finally decided to go with a solution provided here as I found this to be the neatest solution.
I have created a config file called userdata.config in which I have the following code:
module.exports.userdata = {
guest: true,
authenticated: false
};
I am then accessing this in the controller as follows:
sails.config.userdata.authenticated = true;
sails.config.userdata.guest = false;
sails.config.userdata.user = sessionUser;
This is then accessed in the view as follows:
sails.config.userdata.user.firstName
Related
I wanted to know if there is a way to share variables between 2 routes in expressJS without declaring it as a global. Consider I have the following routes:
exports.routeOne = (req,res) => {
var myVariable='this is the variable to be shared';
}
exports.routeTwo = (req,res) => {
//need to access myVariable here
}
How can this be done ?
===============
Note
This answer is answering the wrong question, but it might still be useful for others who also misinterpreted the question like I did, so I'm keeping it here for now.
===============
In order for the variable to exist it would have to first execute routeOne and then execute routeTwo. This is a pretty common scenario with express. To get the details on how this works read up on middleware (http://expressjs.com/en/guide/writing-middleware.html) and understand that each route is middleware.
The common pattern solution here is to add a new property to either the req or res object that stores your variable. Then you tell the route to call the next middleware. The next middleware has access to the same req and res so it also has access to the property and value that you just stored.
There is nothing wrong with this practice as most middleware does it. For example the body-parser middleware (https://www.npmjs.com/package/body-parser).
Here is an example of how your code might run:
routes.js
exports.routeOne = (req,res,next) => {
req.myVariable='this is the variable to be shared'
next()
}
exports.routeTwo = (req,res) => {
//need to access myVariable here
console.log(req.myVariable)
}
index.js
const express = require('express')
const routes = require('./routes.js)
const app = express()
app.use(routes.routeOne)
app.use(routes.routeTwo)
app.listen(3000)
One of the awesome things about NodeJS is that you can store state in variables. The process that runs your NodeJS server continues to run without restarting for each request that comes in. So any variables you store using GLOBAL.myVarName or variables that you store within the scope of a module (a single JavaScript file) will persist... until the process quits. The process could quit if the server goes down or if an Error or other exception is throw that is not caught. You can set up a process manager to keep it running, but your in memory variables are now lost.
I would recommend using variables that are scoped at a higher level for caching, but if the data is important it should be stored to and read from a database as needed.
Here is an example of your routes.js module. Any variable you declare outside of functions are scoped to this file. In other words, the variables are global to this file. You can read up more on that at https://nodejs.org/dist/latest-v8.x/docs/api/modules.html and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures.
routes.js
let variableScopedToModule = 0
exports.routeOne = (req, res) {
variableScopedToModule++
}
exports.routeTwo = (req, res) {
console.log(variableScopedToModule)
}
If it is important that you never loose the variables state, and assuming you are not running the server on multiple processes for load balancing, then you could do something like this:
routes.js
const db = require('./my-db-controller')
let cachedValue
exports.routeOne = async (req, res) {
if (cachedValue === undefined) cachedValue = await db.getValue()
cachedValue++
db.setValue(cachedValue)
}
exports.routeTwo = (req, res) {
console.log(cachedValue)
}
This second solution is more complex because you'll need to understand promises as well as async and await. Also this solution will not work if you are using a load balencer unless you use something like Redis. Alternatively you can just read and write to the database for each request instead of using in memory caching.
For user-specific data that you want to persist from one request to the next, you can't just store data in simple server-side variables because server-side variables are freely shared among all users who make requests of your server.
The usual way to store server-side data that is user-specific is to use a session object. In a nutshell, when a user makes a request, some session middleware (which runs on every request) checks to see if a session cookie exists. If it does not, then one is created. The session cookie servers to uniquely identify that particular user when they make future requests.
Once you have a unique session cookie, the session middleware can create a session object that corresponds with that session cookie. Typically, there is a unique ID in the session cookie that servers as a lookup key for the session object.
Now, whenever that user makes a request of your server, the session middleware will look at the session cookie, get the encrypted session id out of it, decrypt the id, look up the session object that corresponds to that id and then make that session object available to your request handlers, usually in req.session. Your request handlers then have full access to the session object and can add, remove or modify your own properties on that session object.
express-session is common piece of middleware that is used for implementing sessions with nodejs/express. There are plenty of examples for how to use it in the documentation so I won't repeat that all here. Here's one such example in the doc.
By default, express-session uses a memory store (which just means the session objects are kept in memory). This can be fine for simple uses, but it has some limitations and express-session does not recommend it for production use. One major limitation of keeping the sessions in memory is that if the server crashes or restarts, all session data is lost. There are dozens of add-in object stores for express-session, most of which store data more durably such as in a database on disk.
I am kind of new to Express and Node (I come from the front-end development world), so this might be a really stupid question.
Currently, I work on an Express JS app that uses express-session, sessionstore + memcache and cookie-parser for managing sessions.
I have a particular use case wherein I have one session variable (age) that is passed on to every view through a middleware that someone in the team who created the app had written:
response.locals.age = request.session.age
The request.session.age is populated from a UserAccount model that is fetched during login.
Now, this middleware is called before the the request reaches the controller, so by the time I get this in my view, the response.locals.age has already been set, which is displayed in the template as is.
My question is this: The age variable can be reset separately through an Admin interface. But because the session is set only upon login, the change doesn't reflect until I logout and login again. I do get the new age value by fetching the UserAccount model again, but I don't know how to refresh the session with the new value without having to logout and login again. I tried doing this:
req.session.age = res.locals.age = < UserAccountResponse >.age;
But this doesn't seem to work. What is the ideal way to 'force refresh' the session in this scenario in Express along with the mentioned middlewares? Thanks in advance!
I'm using Sails.js framework, and I have User model with login field.
I also have User controller that allows me to send request like
http://localhost:1337/user/:id
And it returns given user data. However I'd prefer to use user login instead of user id so I could use /user/mylogin instead of /user/564a0aacecf0e8fb20c38a4e. Is there any way to do it without creating routes myself (I like how sails handle all default routes including relations like /user/:id/comments and I dont want to rebuild all of those just to use login instead of id)
You could do /user?login=username. This is handled by sails's blueprint api: http://sailsjs.org/documentation/reference/blueprint-api/find-where
If you really want it you can have it. But you have to get your hands a bit dirty. There are a few possibe ways. But I am pointing you out the easiest way seemed to me.
Add a route like following in your config/routes.js.
'/user/:myLogin': {
controller: 'user',
action:'getUserByUserLogin'
}
Add an action named getUserByUserLogin in your
api/controller/UserController.js. You can access the myLogin
value from request object from the controller action with
req.param("myLogin").
module.exports = {
getUserByUserLogin: function(req, res){
var myLogin = req.param("myLogin");
/* Do whatever you want */
}
};
Have been working through the sails cast tutorials and am confused about the way that sessions work.
In the tutorial, the user is marked as authenticated in the session controller by:
req.session.authenticated = true;
req.session.User = user;
res.redirect('/');
Why is the session being saved in the request?! My understanding is that the 'req' object in express.js is the information the browser sends to the server.
Shouldn't the server save this information elsewhere (won't the request object be deleted when theres another request?)
Furthermore, somehow the application retrieves the authentication status from another object session when templating a page with ejs:
<% if (session.authenticated) { %>
why isn't this variable set directly?
Probably a silly question but I am confused at how the logic works and online articles/tutorials aren't helping me understand...
It is common practice for express middleware (remember, Sails is built on express) to attach properties to the req object so it may be accessed in later middleware, and eventually your controllers. What happens behind the scenes is your req object comes in with a cookie containing the session ID, and then the session middleware uses that to retrieve the actual session data from some datastore (by default, and in-memory store is used. Super fast and easy for development, but not recommended for deployment), and then attaches that to the req object.
Regarding the value of session.authenticated in your EJS, by default Sails includes req.session in res.locals (accessible in views), so that value will be whatever is stored in the session via your controller.
The browser sends over the session id which is stored on a cookie. The session object is referenced by that session id which is stored server side. The session is attached to the request (for convenience I suppose). You can read more here https://github.com/expressjs/session#compatible-session-stores
I wouldn't know what is setting session.authenticated without seeing more code.
I'm using Node.js and Express, and I want to pass a local variable in to the layout on every page, is there any way to do this? I'm using Jade as my templating engine.
The reason I want this is because I want to display the user's username on every page (using session), any way to do this other than including it every time in the local object?
You can achieve this by defining a dynamic view helper, as pointed out in the official Express guide:
app.dynamicHelpers({
session: function(req, res){
return req.session;
}
});
Then in your views you can simply access the session variable, and for example session.user to display the user.