In node.js, i have function that before any action is checking if everything is ok at the start of request (validating JSON etc). Almost everything is working ok, but I have one problem. I don't now how to pass object reference using next();
To call checking function I'm using.
app.all('/:action', frontendProcessor.checkSession());
At the middle of this code, I'm using next()
frontendProcessor.checkSession = function(){
return function(req, res, next) {
var inputJson = req.body.JSONVAR || false,
action = req.params.action;
// validation
frontendProcessor.validateJSON(inputJson, afterValidation);
function afterValidation(err, inputData){
if(err) {
global.consoleLog(err);
res.send(fail);
}else{
if(action == 'login' ){
console.log(inputData);
next(inputData); //<< here, how to pass inputData thru next
}else{
mUsers.checkSessionId(email, sessionId, process);
};
};
};
function process(response) {
if(!response){
global.consoleLog("Security Error: Bad session Id.");
var response = JSON.stringify(badSession);
res.send(response);
}else{
global.consoleLog('Security: session ok! next');
next(inputData);
};
};
};
};
next() shouldn't ever pass data because it's just designed to call the next request handler, not a specific request handler. Nirk's comment is correct, you should attach your data to the req object and read it from there when needed.
Related
I'm getting confused with next(); I read through this post which essentially says it passes control to the next route. In my code below, having next(); where it is causes me to get "Cannot set headers after they are sent to the client". However, if I comment that out and then restore the else clause of my if statement, it functions correctly both when an incorrect empID is passed as well as when a correct one is. I'm hoping someone could explain what exactly is happening? Why does the position of the next() matter? It seems like it would be called either way?
I'm trying to do what is happening in this post which is add a value to, say req.user, but I haven't been able to get that to work at all so I'm trying the method I have here.
let checkEmp = (req, res, next) => {
db.get("select * from Employee where id = $id", {$id: req.empID},
(err, row) => {
if (err || row === undefined) {
res.status(404).send();
// } else {
// next();
}
});
next();
};
// get all timesheets
timesheetRouter.get("/", getParams, checkEmp, (req, res, next) => {
if (req.empID) {
db.all("select * from Timesheet where employee_id = $id", {$id: req.empID},
(err, rows) => {
if (err) {
next(err);
} else {
return res.status(200).send({timesheets: rows});
}
});
} else {
return res.status(404).send("Nothing to see here");
}
});
Looks like db.get() is probably asynchronous, so in the example as shown, next() will be called before db.get() finishes and it moves on to the next handler. Then, when the db.get() finishes, it tries to send a response, but the response has already been sent by the anonymous function in the main handler. By moving the next() inside of db.get(), you're essentially waiting for it to finish before moving on.
I'm new to Express.
I am trying to route a request according to a value in DB. To do this I am calling a service function.
Routing is working successfully but I am loosing the request object.
I have tried to pass the req to the service but it didn't work.
Any help showing how to do this would be nice.
Here is my code block.
var companyService = require("services/companyService");
router.use('/', function (req, res, next) {
companyService.isCompanyOnline(req.body.companyCode).then(function (company) {
if (company) {
router.use('/', require("api/controllers/online"));
}
else {
router.use('/', require("api/controllers/offline"));
}
});
next();
});
module.exports = router;
Services.companyService:
function isCompanyOnline(code) {
var deferred = Q.defer();
companies.findOne({ companyCode: code }, function (err, company) {
if (err) deferred.reject(err.name + ': ' + err.message);
//if (err) throw err;
if (company) {
// return company online parameter
deferred.resolve(company.isOnline);
} else {
// company not found
deferred.resolve();
}
});
return deferred.promise;
}
You are losing the request object because you are not passing it anywhere.
I believe your main issue here is the fact that you have three route handlers registering on the same path /. If I am not mistaken they will be all called in-order that you add them. But the order in your depends on the if you are placing the router.use() calls. So it's unpredictable, and likely not going to work as you expect. Once they are registered they stay in the middleware stack until you restart the app.
I suggest you refactor to have the online/offline logic in one place, or register all your middlewares at once, so you know in which order they get called, and use next() accordingly.
On another note, if you want to pass an argument to required modules, do this:
Change api/controllers/online and the other, to accept an argument, and return the handler function that you are passing.
// your code in api/controllers/online and offline
module.exports = function (req) {
// now you have req available in-scope here
function yourFunctionThatNeedsReq (req) {
// do stuff with req
}
return yourFunctionThatNeedsReq;
};
Then update your require like so. Note the …(req).
router.use('/', require("api/controllers/online")(req) );
My solution:
First, define routes:
app.use('/api/online', require("api/controllers/online"));
app.use('/api/offline', require("api/controllers/offline"));
Then change the url in the router :
if (req.body.companyInfo.isOnline) {
req.url = '/online' + req.url + '/' + req.body.companyInfo.companyPath;
next();
}
else {
req.url = '/offline' + req.url + '/' + req.body.companyInfo.companyPath;
next();
}
I've got a node.js-based webserver running at home and i'm trying to implement a login form.
So basically I need to access POSTed data (login/password). I found this :
How do you extract POST data in Node.js?
(req.on('data'...) & req.on('end'...))
But i need to do this asynchronously, can someone tell me how to do that ?
(I need this code to be blocking, not non-blocking)
EDIT: All my code is available on Github :
https://github.com/Fointard/NodeJs/tree/authentication
The problem lies here : https://github.com/Fointard/NodeJs/blob/authentication/js/reqHandlers/auth.js Lines 98 and 104, i'm relying on 'data' and 'end' envents but i'd like to do that asychronously so that checkID() (line 95) is able to return true or false.
You can't. HTTP Requests are I/O operations and will always be resolved asychronously. Your checkID function will never return a value. You need to add a second parameter (usually called callback) that will be called with true or false.
function checkID(req, callback) {
var body = '';
req.on('data', function (data) {
body += data;
if (body.length > 1e6)
req.connection.destroy();
});
req.on('end', function () {
var post = qs.parse(body);
if ('loginInputLogin' in post && 'loginInputPassword' in post) {
console.log('login : '+post['loginInputLogin']);
console.log('password : '+post['loginInputPassword']);
}
if (post['loginInputLogin'] === 'fointard' && post['loginInputPassword'] === 'f01n') {
console.log('ID confirmed');
callback(true);
}
callback(false);
});
}
And use it like so:
checkID(yourRequest, function(success) {
if(success) {
//login successfull
} else {
//login failed
}
})
and thanks to be there.
Issue :
I'm making a tiny mongoose "middleware" to handle a mongoose error :
// callback function called at each mongoDB response
var handleDbRes = function(callback) {
return function (err, entries) {
if (err) {
err.status = 500;
return next(err);
}
return callback(entries) // that line throw the exception
}
};
And so I'm using it into an api endpoint, e.g. :
someRouter.get('/', function(req, res) {
models.article.find(handleDbRes(res.json))
})
With that code, I encounter an error :
TypeError: Cannot call method 'get' of undefined
I followed the exception and looked at res.json() declaration, when debugging, I figured out :
var app = this.app;
var *** = app.get('***') // that line throw the exception
I guess that app is not defined cause app doesn't exists in "this".
Please can you help me to solve this problem ? I think that the reason is simple but I don't get it...
Thanks you for listening ;)
EDIT : I tried to res.json.bind(res) and it worked, as I thought, but that's really awful to bind this way for most api endpoint and I guess there is another way to do that kind of functionality without that.
EDIT : Thanks to Mscdex advices, I modified my code this way :
.get('/', function(req, res, next) {
models.article.find(handleDbRes(res.json.bind(res), next))
...
...
// callback function called at each mongoDB response
var handleDbRes = function(successCallback, errorCallback) {
return function (err, entries) {
if (err) {
err.status = 500;
return errorCallback(err);
}
return successCallback(entries)
}
};
When you pass res.json, the context for the json() function is lost (it no longer knows what this is because it is not bound). So here are a few possible solutions:
Use a bound version of the function so that this inside json() will always evaluate correctly:
someRouter.get('/', function(req, res) {
models.article.find(handleDbRes(res.json.bind(res)))
})
Or use a wrapper function instead:
someRouter.get('/', function(req, res) {
function respondJSON(val) {
res.json(val);
}
models.article.find(handleDbRes(respondJSON))
})
Or just pass in res and call res.json() inside handleDbRes():
someRouter.get('/', function(req, res) {
models.article.find(handleDbRes(res))
})
// callback function called at each mongoDB response
var handleDbRes = function(res) {
return function(err, entries) {
if (err) {
err.status = 500;
return next(err);
}
res.json(entries);
}
};
The other problem is that handleDbRes() doesn't have access to next, so you need to also pass that function in for when you run into an error.
My NodeJS server, using express, has a bunch of entries to specify various routes:
app.post('list_streams.json', auth, stream_handler.list_streams);
app.post('add_stream.json', auth, stream_handler.add_stream);
app.post('delete_stream.json', auth, stream_handler.delete_stream);
etc...
The auth middleware is written like this:
var auth = express.basicAuth(function(user, pass, callback) {
user_handler.authenticate_user(user, pass, callback);
});
Inside the user_handler.authenticate_user() function, an access to the database is performed to validate the user. I'd like to add some statistics and keep track of every access that a particular user performs. I'd like to do this inside the authenticate_user() function, as this is where a database is accessed for the user record and I can use the same access to update the statistics info in the user record, but I need to somehow pass an extra argument to the authenticate_user() specifying the type of access that was performed; either the route itself or some token that identifies the route being accessed. And I can't figure out how to do this. The 'req' is not available inside the authenticate_user() function.
Thank You,
Gary
I'm not sure what you need can be easily done from your authenticate_user function since it's only called once per session at the first access from any user.
The best approach to log ALL access per user would be to create a new middleware function as described at the end of this post.
But assuming you only wish to log user authentications, one way to solve your problem would be to replace express.basicAuth with your own version that binds the callback function to the express req object, like this:
var util=require('util'),
express=require('express'),
app=express(),
auth=basicAuth(function(username,password,next){
console.log('auth has access to req as "this": %s',util.inspect(this));
});
app.get('/',auth,function(req,res){
console.log('in request for "/", req is: %s',util.inspect(req));
res.send('SUCCESS');
});
app.listen(4000,function(){
console.log('running');
});
// Replacement for connect.basicAuth (as used by express)
// lifted from https://github.com/expressjs/basic-auth-connect
function unauthorized(res, realm) { // required by basicAuth
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"');
res.end('Unauthorized');
}
function error(code, msg){ // required by basicAuth
var err = new Error(msg || http.STATUS_CODES[code]);
err.status = code;
return err;
}
// replacement basic auth which binds the callback to the "req" object
function basicAuth(callback, realm) {
var username, password;
// user / pass strings
if ('string' == typeof callback) {
username = callback;
password = realm;
if ('string' != typeof password) throw new Error('password argument required');
realm = arguments[2];
callback = function(user, pass){
return user == username && pass == password;
}
}
realm = realm || 'Authorization Required';
return function(req, res, next) {
var authorization = req.headers.authorization;
// 20140601 RR - !!NOTE!! bind callback to req
callback=callback.bind(req);
if (req.user) return next();
if (!authorization) return unauthorized(res, realm);
var parts = authorization.split(' ');
if (parts.length !== 2) return next(error(400));
var scheme = parts[0]
, credentials = new Buffer(parts[1], 'base64').toString()
, index = credentials.indexOf(':');
if ('Basic' != scheme || index < 0) return next(error(400));
var user = credentials.slice(0, index)
, pass = credentials.slice(index + 1);
// async
if (callback.length >= 3) {
callback(user, pass, function(err, user){
if (err || !user) return unauthorized(res, realm);
req.user = req.remoteUser = user;
next();
});
// sync
} else {
if (callback(user, pass)) {
req.user = req.remoteUser = user;
next();
} else {
unauthorized(res, realm);
}
}
}
}
If you look at the line marked with "!!NOTE!!" above, you'll see that the callback you pass to the new basicAuth function has been bound to express' req request object, which makes its idea of this a reference to the request.
Now all you need to do is reference this.url to get the original request URL and log it.
As mentioned above, one thing to note is that the callback to auth is only called to authenticate the user once.
Subsequent requests already have the req.user HTTP header variable set, so the request is allowed to pass without calling the authentication callback.
This is why the best way to log all interactions for a particular user would be to add your own middleware after the call to auth, such as:
function logUser(req,res,next){
// since this middleware is called AFTER auth, the user is already authorized.
log.info('user "'+req.user+'" called url:'+req.url);
next(); // pass control to the next stage in fulfilling the request
}
app.get('/',auth,logUser,function(req,res){
...
});