I am new to node.js and I try to render different pages based on some conditions, however, I sometimes meet the problem "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client", I think this is because of the asynchronous behavior of node.js. How can I fix this problem?
I tried setTimeout but I think that's not the right way to do that.
The following part is the section where I met this issue:
/*
signup err code:
0 stand for no errors,
1 stand for confirmPassword and password not match
2 stand for user already exist
*/
app.post('/signup',(req, res) => {
//initialize the signupErr to be 0
signupErr = 0;
//get the data from the post request
let username = req.body.username;
let email = req.body.email;
let password = req.body.password;
let confirmPassword = req.body.confirmPassword;
let organization = req.body.organization;
/*first check the twice entered password, if not match set error number to 1 for the server to render the corresponding error message*/
if (password!=confirmPassword){
signupErr = 1;
res.redirect('/signup');
}
//form the query to consult the database to check if user exists
let queryUsername = "SELECT password FROM User WHERE Username = " + "'" + username + "'";
let queryEmail = "SELECT email FROM User WHERE Email = " + "'" + email + "'";
// check if username exists
connection.query(queryEmail,(err,results) => {
if(err){
console.log("Errore login: " + err);
} else if(results.length!=0) {
signupErr = 2;
res.redirect('/signup');
}
});
//check if email exists
connection.query(queryUsername,(err,results) => {
if(err){
console.log("Errore login: " + err);
} else if(results.length!=0) {
signupErr = 2;
res.redirect('/signup');
}
});
// form the insertion query if no err found
let queryInsert = "INSERT INTO `PIA`.`User` (`Username`, `Email`, `Password`,`Organization`) VALUES ('"+username +"', '"+ email +"', '"+password +"', '"+organization + "')";
// insert the data into the database
connection.query(queryInsert,(err,results) => {
if(err){
console.log("Errore login: " + err);
} else{
console.log("Inserted");
res.render('main');
}
});
});
The insertion part will still run sometime after the redirect.
I'll be really grateful if you can give me any ideas on how to solve that.
Related
I dont know what to say, i think code says everything. i just want to render something and this error appears, but i dont know why. I tried so many solutions from stackoverflow, and others that i couldn't count them all. Im using mysql2 lib. Please help.
router.get("/:link", function(req, res){
connection.query(
"SELECT * FROM yourshort WHERE link = '" + req.params.link + "'",
function (err, result, rows, fields){
Object.keys(result).forEach(function(key) {
var row = result[key];
console.log(row.link)
if(row.link = req.params.link) {
res.send(row.link + row.views + rows.created)
return;
} else {
res.send("URL does not exist")
return;
}
});
});
});
just because you send it in foreach you get that error .
do you want to send it just one time and first item of db ? if yes use this :
router.get("/:link", function (req, res) {
var i = 0;
connection.query(
"SELECT * FROM yourshort WHERE link = '" + req.params.link + "'",
function (err, result, rows, fields) {
Object.keys(result).forEach(function (key) {
var row = result[key];
console.log(row.link)
if (row.link = req.params.link) {
if (i === 0) {
res.send(row.link + row.views + rows.created)
i = 1;
}
} else {
if (i === 0) {
res.send("URL does not exist")
i = 1;
}
}
});
});
});
A service that sends your data. You run into problems when you try to return your data in a loop. The good thing about this is to come back after you get all the result you will get from the loop. You can use asynchronous structure for this. Maybe you can try this.
router.get("/:link", (req, res) => {
let myObject = {};
connection.query(
"SELECT * FROM yourshort WHERE link = '" + req.params.link + "'",
async(err, result, rows, fields) => {
await Object.keys(result).forEach(function(key) {
var row = result[key];
if(row.link = req.params.link) {
myObject[key] = {link: row.link, views: row.views, created: rows.created}
return;
} else {
myObject['error'] = 'URL does not exist';
return;
}
});
await res.send(myObject);
});
});
Currently I have deployed my website on Heroku with NodeJS.
I came across a weird problem: some SQL-queries are executed twice on mobile, while I don't have this problem on any desktop. So to test if this really was the case, I did a console.log after every database query. In the heroku log I saw that SQL-queries via my mobile were executed twice.
As you can see below, the request is about if you already follow someone. This request has a lot of nested SQL-queries. I don't really know how to properly structure it and therefore it is possible that the problem lies within that. A string is returned which says something about the new relation between you and the user you're trying to follow/unfollow.
So what I'm trying to do:
Check if you follow the person. If this is the case, unfollow the user. Return.
Then, check if the user has a public or private account. If he/she has a public account, you can directly follow the person. Return.
After that, check if you already try to follow the user with a lock. If this is the case, remove the request. Return.
If (3) is not the case, make a request. Return.
When I make this request on my computer, I looked at the heroku logs which showed the following:
2018-03-31T18:13:18.182665+00:00 app[web.1]: execution 1
2018-03-31T18:13:18.203767+00:00 app[web.1]: execution 3
2018-03-31T18:13:18.204511+00:00 app[web.1]: POST /user/follow 200 55.619 ms - 8
2018-03-31T18:13:18.227543+00:00 app[web.1]: execution 4
However, when I did the exact same thing on my mobile, this was showed:
2018-03-31T18:16:24.776125+00:00 app[web.1]: execution 1
2018-03-31T18:16:24.786991+00:00 app[web.1]: execution 1
2018-03-31T18:16:24.793902+00:00 app[web.1]: execution 3
2018-03-31T18:16:24.794795+00:00 app[web.1]: POST /user/follow 200 54.334 ms - 8
2018-03-31T18:16:24.802526+00:00 app[web.1]: execution 3
2018-03-31T18:16:24.803279+00:00 app[web.1]: POST /user/follow 200 46.972 ms - 8
2018-03-31T18:16:24.816062+00:00 app[web.1]: execution 4
2018-03-31T18:16:24.821743+00:00 app[web.1]: execution 4
To make it clear which SQL-statement executed, I made a console.log for each of them with a different name. ('execution1', 'execution2', etc.).
// Check the relationship between the user and the search-user
router.post('/follow', function(req, res, next) {
// Check if the user is logged in
if (!req.session.user) {
return;
}
// Create connection with database
pool.getConnection(function (err, database) {
if (err) throw err;
// Check if you already follow the user
var sql = "SELECT * FROM Followers WHERE user = " + mysql.escape(req.session.user) + " AND following = " + mysql.escape(req.session.userSearch);
database.query(sql, function(err, result, fields) {
console.log("execution 1");
if (err) throw err;
// You follow the user if the if-statement is true
if (result.length != 0) {
// Unfollow the person
var sql = "DELETE FROM Followers WHERE user = " + mysql.escape(req.session.user) + " AND following = " + mysql.escape(req.session.userSearch);
database.query(sql, function(err, result, fields) {
console.log("execution 2");
if (err) throw err;
database.release();
});
return res.send("FOLLOW");
};
// Check if the person has a lock on its account
var sql = "SELECT private FROM Users WHERE user = " + mysql.escape(req.session.userSearch);
database.query(sql, function(err, result, fields) {
console.log("execution 3");
if (err) throw err;
// If the account is public, then the if-statement below is true
if (result[0].private == "0") {
// Follow if the account is not private
var sql = "INSERT INTO Followers (user, following) VALUES (" + mysql.escape(req.session.user) + ", " + mysql.escape(req.session.userSearch) + ")";
database.query(sql, function(err, result, fields) {
console.log("execution 4");
if (err) throw err;
database.release();
});
return res.send("UNFOLLOW");
}
// Prepare data
var sql = "SELECT * FROM PendingFollowers WHERE user = " + mysql.escape(req.session.user) + " AND following = " + mysql.escape(req.session.userSearch);
database.query(sql, function(err, result, fields) {
console.log("execution 5");
if (err) throw err;
// Check if you already try to follow the person
if (result.length == "1") {
// Remove the request
var sql = "DELETE FROM PendingFollowers WHERE user = " + mysql.escape(req.session.user) + " AND following = " + mysql.escape(req.session.userSearch);
database.query(sql, function(err, result, fields) {
console.log("execution 6");
if (err) throw err;
database.release();
});
return res.send("FOLLOW");
}
// Follow and put it in pending
var sql = "INSERT INTO PendingFollowers (user, following) VALUES (" + mysql.escape(req.session.user) + ", " + mysql.escape(req.session.userSearch) + ")";
database.query(sql, function(err, result, fields) {
console.log("execution 7");
if (err) throw err;
database.release();
return res.send("REQUESTED");
});
});
});
})
});
});
EDIT:
HTML:
<button class="buttonFollow" ng-click="follow()"> {{ status }} </button>
AngularJS:
// Status follower
$http.get('/user/status').
then(function(res) {
$scope.status = res.data;
});
// Follow someone
$scope.follow = function() {
$http.post('/user/follow').
then(function(res) {
$scope.status = res.data;
$http.get('/user/informationFollower').
then(function(res){
$scope.following = res.data[0].following;
$scope.followers = res.data[0].followers;
});
});
};
The HTML/AngularJS above is from the page of the user you want to follow. So when the follow-request is done, the number of followers/following of the targeted user will be updated. When I try to follow someone via my computer, the number is incremented with 1 (which is OK). But via my phone, it is incremented with 2. It created a duplicate follow-row in my database on table Followers, but the id is not the same.
Hi I need help figuring out how to get my string entry in my database into a variable. All of my code works and if I copy and paste the exact string from my database into my bcrypt compare call it works perfectly. Basically my problem is that my variable "compare" prints out as
[{"password": "$2a$10$eilJb6SGKSSQm2b.U5tj.ut4o8.oHyJNVhbkpjcpeomCj4GlMEyqC"}]
but I just need
"$2a$10$eilJb6SGKSSQm2b.U5tj.ut4o8.oHyJNVhbkpjcpeomCj4GlMEyqC". Any ideas?
account.on("end", function(result) {
if(result.rows.length > 0){
console.log("found account: ");
console.log(JSON.stringify(result.rows, null, " "));
//turn the db entry into a string
compare = JSON.stringify(result.rows, null, " ");
//compare the hashed password from database to parameter for password change
bcrypt.compare(password, compare, function(err, res) {
console.log("res result: " + res);
if(res){
console.log("passwords are same");
//new query updating the password in the db
if(person=="s"){
//update db
client.query('UPDATE students SET password=($2) WHERE email=($1)',
[req.body.email, hash_new]);
console.log("changed students password");
} else {
//update db
client.query('UPDATE teachers SET password=($2) WHERE email=($1)',
[req.body.email, hash_new]);
console.log("changed teacher password");
}
} else {
console.log("passwords are not the same");
}
});
} else {
console.log("account not found!");
}
});
Instead of
compare = JSON.stringify(result.rows, null, " ");
try
compare = result.rows[0].password;
You don't need the JSON string but the JavaScript value.
app.post('/register', function(req,res){
console.log("register post got", req.body)
if (req.body.username && req.body.password) {
db.get("SELECT * FROM Users WHERE username = '" + req.body.username + "' LIMIT 1");
res.end("Account already exists");
var stmt = db.prepare("INSERT INTO user VALUES (?,?)");
stmt.run(req.body.username, req.body.password);
res.end("OK");
} else {
res.end("username and password are requesiraehri");
}
});
What's wrong here, because If I'm trying to register user, it says it already exists while the db is completely empty.
Any help?
In all that follows, I assume you are using this sqlite library: https://www.npmjs.com/package/sqlite3
res.send("Account already exists"); is called unconditionally. You forgot to check the result of your query.
But there are other issues in your code. Frist of all, you are not using async functions with callbacks. db.get is an asynchronous function and takes a callback as a second argument, which receives either the first row of the resultset or an error (see documentation).
app.post('/register', function(req,res){
console.log("got register post", req.body)
if (req.body.username && req.body.password) {
db.get("SELECT * FROM Users WHERE username = ? LIMIT 1", req.body.username, function(err, row){
if(row){
res.end("Account already exists");
} else {
var stmt = db.prepare("INSERT INTO user VALUES (?,?)");
stmt.run(req.body.username, req.body.password);
res.end("OK");
}
}
} else {
res.end("username and password are required");
}
});
stmt.run is also asynchronous. So be aware, that res.end('OK') is executed before the data is inserted into the database. It would be probably better to check if the insert statement is run successfully before sending the response.
Another important issue is that you insert the password in clear in the database. You should think about crypting it. Never store passwords in cleartext !
I need to authenticate a user, so I call this function:
db.authenticate = function(username,password)
{
var query = this.query("SELECT * FROM users WHERE username = '"+username.toLowerCase()+"' AND password = crypt('"+password+"',password);");
var found = false;
query.on('row', function(row) {
found = true;
console.log("Found:",row.username);
});
if(found){return true;}else{return false;}
}
The problem is, that the function doesn't wait for query.on('row') to finish and just return false..
Any ideas how to solve this?
Since it's an asynchronous function, you need to get your results from a callback.
db.authenticate = function(username, password, callback) {
var query = this.query("SELECT * FROM users WHERE username = '" + username.toLowerCase() + "' AND password = crypt('" + password + "',password);");
query.on('row', function(row) {
console.log("Found:", row.username);
return callback(true);
});
callback(false);
}
Then you'd call it like so:
db.authenticate(username, password, function (isAuthenticated) {
console.log(isAuthenticated); //Will be either true or false.
}