I'm trying to add a basic authentication to my Express paths. To check if the combination user/pass is correct, I read the collections usermodels in my MongoDB database.
Here is my code :
server.js
var normal = express.basicAuth(normalAuth);
app.get('path', normal, function(req, res){etc.});
function normalAuth(user, pass) {
var mongo = require('./database/userManager');
var result = false;
mongo.start(mongo.list(function(err, data) {
if(err) { throw err; }
else {
console.log('Getting list from db...');
for(var i in data) {
console.log('Getting details from db...');
if(user == data[i].name && pass == data[i].pass) {
console.log('New connection : ' + user);
result = true;
break;
}
else {
console.log('Failed connection : ' + user);
result = false;
}
}
console.log('OK');
}
mongo.mongoose.connection.close();
}));
return result;
}
userManager.js
var mongoose = require('mongoose');
var userModel = mongoose.connection.model('usermodel', new mongoose.Schema({
name: String,
pass: String,
status: { type: Boolean, default: false }
}), 'usermodels');
function start(callback) {
mongoose.connect('mongodb://localhost/derp', function(err) {
if(err) { throw err; }
else {
console.log('Connected to database ');
}
});
}
function list(callback) {
userModel.find(callback);
}
The authentication almost works, it connects to the db and get the informations. It prints on the console
"New connection : /*user connected*/".
But there is a problem : my function normalAuth always return false, so even if I have correct user/pass, I can't access the pages and I have the connection window as if I gave wrong user/pass. I guess it is because this function return the result before my mongo.start(blabla) finished, but I don't know what to do.
Thank you for your help.
You always get false result because in Node.js every IO operation is asynchronous.
Let me explain: When you call mongo.start(...) function, Node.js will put that in another operation queue because it is IO operation. Main operation queue must not be blocked, and that is why IO operations are always asynchronous. Ater mongo function is put in another queue of operations, your main operation queue will continue and get to the line of code: return false. And it will return false regardless what function mongo.start(...) will do with it.
Solution is by sending callback function:
function normalAuth(user, pass, callbackResult) {
var mongo = require('./database/userManager');
mongo.start(mongo.list(function(err, data) {
if(err) { throw err; }
else {
console.log('Getting list from db...');
for(var i in data) {
console.log('Getting details from db...');
if(user == data[i].name && pass == data[i].pass) {
console.log('New connection : ' + user);
// here you send true to callback function
callbackResult(true);
break;
}
else {
console.log('Failed connection : ' + user);
// here you send false to callback function
callbackResult(false);
}
}
console.log('OK');
}
mongo.mongoose.connection.close();
}));
}
And you call this function like this:
normalAuth(user, pass, function(result) {
if(result)
console.log("Auth OK");
else
console.log("Auth Failed");
}
EDIT: In express.basicAuth function it will be called like this:
app.use(express.basicAuth(function(user, pass, callback) {
normalAuth(user, pass, function(result) {
callback(null /* error */, result);
}
}));
Related
I am running a cron job with node with mongodb as the database. I am trying to close db connection and exit the process once the curr_1 each loop has executed completely.
However the exit() is called while function_2 is being executed. I understand this is due to the callback and is async in nature.
How do I make sure exit is called only once the curr_1.each is complete?
Any solution without promises?
function function_1(obj){
var curr_1 = coll_1.find({})
curr_1.each(function(err, doc) {
function_2(doc)
});
exit(obj)
}
function function_2(obj) {
coll_2.findOne({}, function(err, document) {
dosomeprocess(obj)
})
}
function exit(obj) {
// Close connection
console.log('closing connection')
obj.db.close();
process.exit();
}
It's a job for Node async....
For example:
async.each(
curr_1, // the collection to iterate over
function(doc, callback) { // the function, which is passed each
// document of the collection, and a
// callback to call when doc handling
// is complete (or an error occurs)
function_2(doc);
},
function(err) { // callback called when all iteratee functions
// have finished, or an error occurs
if (err) {
// handle errors...
}
exit(obj); // called when all documents have been processed
}
);
Without using any library:
function function_1(obj, callback) {
var curr_1 = coll_1.find({})
curr_1.each(function(err, doc) {
callback(err, doc);
});
}
function function_2(err, obj) {
coll_2.findOne({}, function(err, document) {
dosomeprocess(obj)
exit(err, obj);
})
}
function exit(err, obj) {
// Close connection
console.log('closing connection')
obj.db.close();
process.exit();
}
function_1(obj, function_2);
Using async module
var async = require('async');
async.waterfall([
function function_1(callback) {
var curr_1 = coll_1.find({})
curr_1.each(function(err, doc) {
if (err) {
callback(err, null)
} else {
allback(null, doc)
}
});
},
function function_2(obj, callback) {
coll_2.findOne({}, function(err, document) {
if (err) {
callback(err, null);
} else {
dosomeprocess(obj)
callback(null, obj);
}
})
}
], function done() {
obj.db.close();
process.exit();
});
Simply give a condition in your loop using counter.
function function_1(obj){
var curr_1 = coll_1.find({})
var curr_1Length = curr_1.length;
var counter = 0;
curr_1.each(function(err, doc) {
++counter;
//Check condition everytime for the last occurance of loop
if(counter == curr_1Length - 1){
exit(obj)
}
function_2(doc)
});
}
Hope it helps :)
I am creating a 'refresh data' function in Node and I cannot figure out where to place the callbacks and returns. The function continues to run. Below is a list of things the function should do. Could someone help out?
Check if a user has an api id in the local MongoDB
Call REST api with POST to receive token
Store token results in a MongoDB
Terminate function
./routes/index.js
router.post('/refresh', function(req, res) {
var refresh = require('../api/refresh');
refresh(req, function() { return console.log('Done'); });
});
../api/refresh.js
var callToken = require('./calltoken');
var User = require('../models/user'); // Mongoose Schema
module.exports = function(req, callback) {
User.findOne( {'username':req.body.username}, function(err, user) {
if(err) { console.log(err) }
if (user.api_id == 0) {
callToken.postToken(req.body.username, callback);
} else { // Do something else }
});
};
./calltoken.js
var request = require('request');
var Token = require('../models/token'); // Mongoose Schema
module.exports = {
postToken: function(user, callback) {
var send = {method:'POST', url:'address', formData:{name:user} };
request(send, function(err, res, body) {
if(err) { console.log(err) }
if (res.statusCode == 201) {
var newToken = new Token();
newToken.token = JSON.parse(body).access_token['token'];
newToken.save(function(err) {
if(err) { console.log(err) }
return callback();
});
}
});
}
};
I'm not an expert in Express but everywhere in you code in lines with if(err) { console.log(err) } you should stop execution (maybe of course not - up to you app) and return 400 or 500 to client. So it can be something like
if(err) {
console.log(err);
return callback(err); // NOTICE return here
}
On successful execution you should call return callback(null, result). Notice null as a first argument - it is according nodejs convention (error always goes as first argument).
I am mew to node js, I have something like this,
get_contacts(data, function(contacts) {
if (contacts.length) {
var count = contacts.length;
for (var i = 0; i < count; i++) {
result = {
id: contacts[i].id,
name: contacts[i].name,
sent1: get_sent(data.userId, contacts[i].id, function(resp) {
result.sent = resp.count;
}),
}
result1[i] = result;
}
output = {
contacts: result1,
}
} else {
output = {
error: "No Contacts.",
}
}
res.writeHead(200, {'content-type': 'text/html'});
res.end(JSON.stringify(output));
});
get_contacts is a callback function which will return contact list.result1 & result are objects. Now value for sent should come from a function get_sent, and get sent is like this
function get_sent(userId, contactId, callback) {
pool.getConnection(function(err, connection) {
connection.query("my query here", function(err, rows) {
connection.release();
if (!err) {
callback(rows);
} else {
console.log(err)
}
});
});
}
But im not getting any value since nodejs. since nodejs is async it is not waiting for the function to return value. I know, im doing it in wrong way. Please help
You need to use a callback. In simple words is a function that you'll execute after something happens. You should read more about that. You should get a book about javascript but you can start reading here for example.
About your case, you could solve it like this
//Asumming that you object `result` is global.
result = {
id: contacts[i].id,
name: contacts[i].name,
sent: -1 //Some default value
}
//Just to put the code into a function, you have to put it where you need
function constructObject (){
get_sent(uId, cId, function(err, total){
if(err){
console.log("Something was wrong.", err);
}
result.sent = total;
//Here you have your object completed
console.log(result);
});
}
//You need to use a callback
function get_sent(uId, cId, callback) {
pool.getConnection(function(err, connection) {
//Note that I add an alias here
connection.query("SELECT count(*) as total FROM table_name", function(err, rows) {
connection.release();
if (!err) {
//I am returning the result of the query and a null error
callback(err, rows[0].total);
} else {
console.log(err);
//I am returning an error
callback(err);
}
});
});
}
//For example you could call this function here
constructObject();
And it depends of what are you doing exactly but Maybe you need a callback on your constructObject too.
I am trying to parse an object from a javascript (a blog post head and body) through a node.js server and on to save it in the mongoDB.
this is the parsing code:
function saveState( event ) {
var url = '';
var postTitle = headerField.innerHTML;
var article = contentField.innerHTML;
var post = {
title: postTitle,
article: article
};
var postID = document.querySelector('.save').getAttribute('id');
if(postID != "new"){
url += "?id=" + postID
}
var request = new XMLHttpRequest();
request.open("POST", "draft" + url, true);
request.setRequestHeader("Content-Type", "application/json");
request.send(post);
}
this is sent to this node server handler:
app.post('/draft', routes.saveDraft);
exports.saveDraft = function(req, res){
var id = url.parse(req.url, true).query.id;
var post = db.getPostByID(id);
if(id){
console.log('post id' + id);
db.savePost(id, req.body.head, req.body.article);
}
else{
db.newPost(req.body.head, req.body.article);
}
res.render('editDraft.hbs', post); //send the selected post
};
and then, sent to one of these DB functions:
exports.newPost = function (postTitle, article) {
new postCatalog({title:postTitle,
_id:1,
author:'temp',
AuthorID:2,
date:'2/3/12',
inShort:article.substring(0,100),
content:article ,
published:false
}).save(function (err, login) {
if (err) {
return console.log('error');
}
else {
console.log('Article saved');
}
});
}
exports.savePost = function (id, postTitle, article) {
postCatalog.find({_id: id}).save(function (err, login) {
if (err) {
return console.log('error');
}
else {
console.log('Draft saved');
}
});
}
now, I just can't get this to work..
I am new to node and I could really use your help!
thanks
EDITED:
the parameters being sent to the DB saving functions were not written properly.
but i'm still stuck in the same place, where the data is being sent but not saved correctly. I think there's something wrong with my getPostByID function but I can't figure it out:
exports.getPostByID =function (id) {
var post = postCatalog.find({_id: id}, function(err, post){
if(err) {
return handleError(err);
}
else{
if(post > 0){
post = post[0];
}
return post;
}
});
return post;
}
I am using express (including bodyparser) and mongoose. view engine is hbs.
thanks again.
You have to write it the asynchronous way, e.g. your getPostByID:
exports.getPostByID = function (id, callback) {
postCatalog.find({_id: id}, function(err, post) {
if (err) {
callback(err);
}
else if (post && post.length > 0) {
callback(null, post[0]);
}
else {
callback(null, null); // no record found
}
});
};
And this is true for your whole code. It's totally different and the way you tried it will never work under Node.js.
BTW there is a mongo-driver method findOne which is better suited in this special case but I didn't want to change your code too much.
I have a script in nodeJS that connects to my postgres DB, the issue is that on a callback method I'm initiating a variable but it seems always empty, this my app.js script :
var ck_username = /^[A-Za-z0-9_]{1,20}$/;
function fncCheckUsernameAvailability(vstrUsername, callback) {
var pg = require("pg");
var client = new pg.Client({user: 'xxx', password: 'xxx', database: 'xxx', host: 'xxx.com'});
client.connect(function(err) {
if (err) {
callback(err, null);
return;
}
client.query("SELECT username FROM users WHERE username ='"+vstrUsername+"'", function(err, result) {
if (err) {
callback(err, null);
return;
}
if (result.rows[0] == undefined) {
callback(null, null);
}else {
callback(null, 'Username already taken');
}
client.end();
});
});
}
app.post("/Signup", function(req, res){
var username = req.body.username;
var usernameError = '';
var errors = [];
if (!ck_username.test(username)) {
errors[errors.length] = "Invalid username";
usernameError = "Invalid username";
}
if (ck_username.test(username)) {
//The issue starts here
fncCheckUsernameAvailability(username, function(err, result) {
if(result != null){
errors[errors.length] = result;
usernameError = result; // usernameError is always empty
console.log(result);//This is printed in console, I can see the result
}
});
}
if (errors.length > 0) {
res.render('Signup.ejs', {
layout:false,
usernameError:usernameError // usernameError is sent to the view empty
});
}
else{
res.render('Signup.ejs', {
layout:false,
usernameError:'No errors'
});
}
});
The result is displayed on cansole so Can someone tell me why this variable is not instantiated , is this callback an asynchronous one?
is this callback an asynchronous one?
Yes.
//This line executes at time 1
if (ck_username.test(username)) {
//The issue starts here
fncCheckUsernameAvailability(username, function(err, result) {
//this section of code executes at time 3! Oh snap async blew my mind!
if(result != null){
errors[errors.length] = result;
usernameError = result; // usernameError is always empty
console.log(result);//This is printed in console, I can see the result
}
});
}
//This section of code executes at time 2 which is BEFORE time 3
if (errors.length > 0) {
res.render('Signup.ejs', {
layout:false,
usernameError:usernameError // usernameError is sent to the view empty
});
}
You have to move ALL the code that requires the result variable INSIDE that callback function. Putting it outside the function but lower in the file makes things execute in the wrong order.
Also checkout http://callbackhell.com for guidance on adjusting to code organization in an async environment.
Yes, the callback is asynchronous, which can execute at any time. It is probably then not yet defined at the time you are accessing. Therefore, you should access the variable from inside the callback, so it will always be defined at that point.
app.post('/Signup', function (req, res) {
var username = req.body.username;
if (!ck_username.test(username)) {
res.render('Signup.ejs', {
layout: false,
usernameError: 'Invalid username'
});
} else {
fncCheckUsernameAvailability(username, function (err, result) {
if (result != null) {
res.render('Signup.ejs', {
layout: false,
usernameError: result
});
} else {
res.render('Signup.ejs', {
layout: false,
usernameError: 'No errors'
});
}
});
}
});
Here is an example of how callbacks work, and why your variable didn't return the expected value. Take these two functions:
var foo = function(num, callback) {
setTimeout(function() {
callback(num * 2);
}, 5000);
};
This function multiplies the number by two, but after 5 seconds. If you try to access the variable before those 5 seconds, you don't have a value, like this case:
var num = 0;
foo(5, function(result) {
num = result;
});
console.log(num); // returns 0
In this case, num is only set to 10 after 5 seconds, console.log() will have executed before that time has passed.