I'm following a course about node.js on Udemy which is kinda outdated and came across these errors which I'm unable to find a solution for.
What I tried:
using next();
adding return res inside all if statements
Can someone help me fix these? I would greatly appreciate it!
Thanks in advance!
Username exists error: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:526:11)
at ServerResponse.header (C:\Users\Documents\projects\chat-app\chat-app-backend\node_modules\express\lib\response.js:771:10) at ServerResponse.send (C:\Users\Documents\projects\chat-app\chat-app-backend\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (C:\Users\Documents\projects\chat-app\chat-app-backend\node_modules\express\lib\response.js:267:15)
at C:\Users\Documents\projects\chat-app\chat-app-backend\controllers\auth.js:38:56
at processTicksAndRejections (internal/process/task_queues.js:97:5) {
code: 'ERR_HTTP_HEADERS_SENT'
}
module.exports = {
CreateUser(req, res) {
const schema = Joi.object().keys({
username: Joi.string().min(5).max(15).required(),
email: Joi.string().email().required(),
password: Joi.string().min(5).required()
});
const {error, value} = Joi.validate(req.body, schema);
if (error && error.details) {
return res.status(HttpStatus.BAD_REQUEST).json({message: error.details});
}
async function EmailExists() {
return await User.findOne({email: Helpers.lowerCase(req.body.email)}) != undefined;
}
async function UsernameExists() {
return await User.findOne({username: Helpers.firstLetterUppercase(req.body.username)});
}
EmailExists().then(exists => {
if (exists) {
return res.status(HttpStatus.CONFLICT).json({message: 'Email already exists'});
}
}).catch((err) => console.log('Email exists error: ', err));
UsernameExists().then(exists => {
if (exists) {
return res.status(HttpStatus.CONFLICT).json({message: 'Username already exists'}) != undefined;
}
}).catch((err) => console.log('Username exists error: ', err));
return BCrypt.hash(value.password, 10, (error, hash) => {
if (error) {
return res.status(HttpStatus.BAD_REQUEST).json({message: 'Error hashing password'});
}
const body = {
username: Helpers.firstLetterUppercase(value.username),
email: Helpers.lowerCase(value.email),
password: hash
};
User.create(body).then((user) => {
res.status(HttpStatus.CREATED).json({message: 'User created successfully'});
}).catch((err) => {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({message: 'Something went wrong. Could not save user'});
});
});
}
}
You're executing some promise and don't wait for the answer before executing the next code....
There are many ways to handle this, this next code is just one way...
const hash = () => BCrypt.hash(value.password, 10, (error, hash) => {
if (error) {
return res.status(HttpStatus.BAD_REQUEST).json({message: 'Error hashing password'});
}
const body = {
username: Helpers.firstLetterUppercase(value.username),
email: Helpers.lowerCase(value.email),
password: hash
};
User.create(body).then((user) => {
res.status(HttpStatus.CREATED).json({message: 'User created successfully'});
}).catch((err) => {
res.status(HttpStatus.INTERNAL_SERVER_ERROR).json({message: 'Something went wrong. Could not save user'});
});
});
EmailExists().then(exists => {
if (exists) {
return res.status(HttpStatus.CONFLICT).json({message: 'Email already exists'});
}
UsernameExists().then(exists => {
if (exists) {
return res.status(HttpStatus.CONFLICT).json({message: 'Username already exists'}) != undefined;
}
return hash();
}).catch((err) => console.log('Username exists error: ', err));
}).catch((err) => console.log('Email exists error: ', err));
Related
I get this error when I try and send a POST request to the server:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:371:5)
at ServerResponse.setHeader (node:_http_outgoing:576:11)
at ServerResponse.header (D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\node_modules\express\lib\response.js:794:10)
at ServerResponse.send (D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\node_modules\express\lib\response.js:174:12)
at ServerResponse.json (D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\node_modules\express\lib\response.js:278:15)
at D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\src\api\endpoints\account\create.js:35:36
at D:\Users\William\OneDrive - William Harrison\User\Documents\Coding\Social App\node_modules\mongoose\lib\model.js:5214:18
at processTicksAndRejections (node:internal/process/task_queues:78:11) {
code: 'ERR_HTTP_HEADERS_SENT'
}
The router and routes are working fine it's just the endpoint.
It will respond to the client with {code:"EMAIL_TAKEN"} which is expected but then it also tries to send the USERNAME_TAKEN thing
I have tried moving parts of the code into separate files and using different status codes.
Code:
module.exports = async (req, res) => {
const schema = require("../../models/account");
const username = req.headers.username;
const email = req.headers.email;
if(!username) {
return res.status(400).json({ "code": "NO_USERNAME" });
}
if(!email) {
return res.status(400).json({ "code": "NO_EMAIL" });
}
if(username.length > 20) {
return res.status(400).json({ "code": "USERNAME_TOO_LONG" });
}
schema.findOne({ username }, async (err, data) => {
if(err) {
return res.status(500);
}
if(data) {
return res.status(400).json({ "code": "USERNAME_TAKEN" });
}
})
schema.findOne({ email }, async (err, data) => {
if(err) {
return res.status(500);
}
if(data) {
return res.status(400).json({ "code": "EMAIL_TAKEN" });
}
})
schema.findOne({ username, email }, async (err, data) => {
if(err) {
return res.status(500);
}
if(!data) {
data = new schema({
username,
email
})
await data.save();
return res.status(200).json({ "code": "CREATED_ACCOUNT" });
}
})
}
Using return in the findOne() callbacks won't stop the other schema.findOne() calls from being made. I'd recommend awaiting on Mongoose's promise API instead to keep everything nice and flat
module.exports = async (req, res) => {
const schema = require("../../models/account");
// headers? 🤔
const username = req.headers.username;
const email = req.headers.email;
if (!username) {
return res.status(400).json({ code: "NO_USERNAME" });
}
if (!email) {
return res.status(400).json({ code: "NO_EMAIL" });
}
if (username.length > 20) {
return res.status(400).json({ code: "USERNAME_TOO_LONG" });
}
try {
if (await schema.exists({ username })) {
return res.status(409).json({ code: "USERNAME_TAKEN" });
}
if (await schema.exists({ email })) {
return res.status(409).json({ code: "EMAIL_TAKEN" });
}
const data = new schema({
username,
email,
});
await data.save();
return res.status(201).json({ code: "CREATED_ACCOUNT" });
} catch (err) {
console.error(err);
res.sendStatus(500);
}
};
On my comment about sending your username and email values via headers... typically you would send them via a POST request in the request body.
For example, client-side...
fetch("/accounts", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ username, email }),
})
and server-side...
const { username, email } = req.body;
You just need to include the appropriate request body middleware, eg
app.use(express.json());
I have seen this error when I tried to change my password in my reactjs and nodejs project!
I tried to enter the old password wrong to check the error message, but it crashed the server. with this:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
Here is the code:
router.put("/changepassword", validateToken, async (req, res) => {
const { oldPassword, newPassword } = req.body;
// console.log("change password", oldPassword);
const user = await Users.findOne({ where: { fullName: req.user.fullName } });
bcrypt.compare(oldPassword, user.password).then(async (match) => {
if (!match) res.json({ error: "Wrong Password Entered!" });
bcrypt.hash(newPassword, 10).then((hash) => {
Users.update(
{ password: hash },
{ where: { fullName: req.user.fullName } }
);
res.json({ message: "SUCCESS" });
});
return;
});
});
this is the client-code:
const changePassword = () => {
axios
.put(
`${targetServer}/auth/changepassword`,
{
oldPassword: oldPassword,
newPassword: newPassword,
},
{
headers: {
accessToken: localStorage.getItem("accessToken"),
},
}
)
.then((res) => {
// console.log("res.data", res.data);
if (res.data.error) {
toast.error("Something went wrong, please try again!", {
className: "error-toast",
draggable: true,
position: toast.POSITION.TOP_CENTER,
});
history.push("/");
// console.log("res.data.error", res.data.error);
}
});
};
Does any suggestions, please?
add else after if
because the server send res.json({ error: "Wrong Password Entered!" })
and then it wants to send res.json({ message: "SUCCESS" })
router.put("/changepassword", validateToken, async (req, res) => {
const { oldPassword, newPassword } = req.body;
// console.log("change password", oldPassword);
const user = await Users.findOne({ where: { fullName: req.user.fullName } });
bcrypt.compare(oldPassword, user.password).then(async (match) => {
if (!match) res.json({ error: "Wrong Password Entered!" });
else {
bcrypt.hash(newPassword, 10).then((hash) => {
Users.update(
{ password: hash },
{ where: { fullName: req.user.fullName } }
);
res.json({ message: "SUCCESS" });
});
}
return;
});
});
This error occurs when you code process something after sending the response to client.
Here in your case, what's happening is, that you haven't return res.json() in the first if statement due to which bcrypt start processing the after response had been sent.
or else add if .. else
Also user.Update being a promise you have to move res.json() in another then chain
like this
bcrypt.compare(oldPassword, user.password).then(async (match) => {
if (!match) res.json({ error: "Wrong Password Entered!" });
bcrypt.hash(newPassword, 10).then((hash) => {
Users.update(
{ password: hash },
{ where: { fullName: req.user.fullName } }
);
}).then(() => res.json({ message: "SUCCESS" }) )
});
Add the else code.
if (!match) res.json({ error: "Wrong Password Entered!" });
else // write code here
if not add else code.
When there is no match variable, the response occurs once and the next code is also executed, so it is an error that the response overlaps. To solve this, add an else statement.
is your code fix
router.put("/changepassword", validateToken, async (req, res) => {
const { oldPassword, newPassword } = req.body;
// console.log("change password", oldPassword);
const user = await Users.findOne({ where: { fullName: req.user.fullName }
});
bcrypt.compare(oldPassword, user.password).then(async (match) => {
if (!match) res.json({ error: "Wrong Password Entered!" });
else {
bcrypt.hash(newPassword, 10).then((hash) => {
Users.update(
{ password: hash },
{ where: { fullName: req.user.fullName } }
);
res.json({ message: "SUCCESS" });
});
}
return;
});
});
I am trying to implement the validation of password change and the issue I have is that I am not getting errorMessage back from the server in case of an error. I have managed to get it work and send back response after the password was updated. Also, I can console.log the error message on the back end but it's not returning an object with errorMessage to the front end.
if (!currentPassword) {
console.log("no current password");
return res
.status(400)
.json({ errorMessage: "Please confirm your current password" });
}
On the front code looks like this:
handleSubmit = (event) => {
event.preventDefault();
const authorization = localStorage.getItem("accessToken");
axios
.put(
`${process.env.REACT_APP_SERVER_URL}/settings/password`,
this.state.user,
{
headers: {
authorization,
},
}
)
.then((res) => {
if (res.errorMessage) {
console.log(res, "Unsuccessful password updated");
} else {
console.log("updating - res:", res);
this.setState({
user: res.data,
});
}
})
.catch((err) => {
console.log(err, "ERROR");
});
};
Everytime there is an error, I am not consol login the actual erroMessage but it is being catched in catch. What is the cause of that?
Thanks
Not a direct res its available under res.data.
Response schema of axios
use
if (res.data.errorMessage) {
instead of
if (res.errorMessage) {
For better understanding you need to console.log(res). Then you could understand the structure of the response
router.put("/password", isLoggedIn, (req, res, next) => {
const { currentPassword, newPassword, newPasswordConfirm } = req.body;
User.findById(req.user._id)
.then((user) => {
bcrypt.compare(currentPassword, user.password).then((isSamePassword) => {
if (!isSamePassword) {
console.log(
"Incorrect current password. To change your password try again!"
);
return res.status(400).json({
errorMessage:
"Incorrect current password. To change your password try again!",
});
}
return bcrypt
.genSalt(saltRounds)
.then((salt) => bcrypt.hash(newPassword, salt))
.then((hashedPassword) => {
User.findByIdAndUpdate(
req.user._id,
{ password: hashedPassword },
{ new: true }
)
.then((user) => {
console.log("user's password successfully changed");
res.status(200).json(user);
})
.catch((err) => {
res.status(500).json({ errorMessage: err.message });
});
})
.catch((err) => {
res.status(500).json({ errorMessage: err.message });
});
});
})
.catch((err) => {
console.log(err);
res.status(500).json({ errorMessage: err.message });
});
});
Im currently writing a RESTful API for a webservice but im having trouble. Im trying to delete an mail, but first i want to check if the mail even exists. My problem is that it doesn't check if mail is null and doesn't respond with a 404. Im working with express and mongoose
router.delete('/:id', (req, res) => {
const { id } = req.params;
Mail.findById(id)
.exec()
.then((mail) => {
if (!mail) {
console.log(mail) // returns null
return res.status(404);
}
})
.then(
Mail.deleteOne({ _id: id })
.exec()
.then(() => {
res.status(200).json({
message: 'Mail deleted',
});
})
.catch((err) => {
res.status(500).json({ error: err });
})
);
});
I think you have to do the the deletion part of the code inside the first then block as an else statement. You are not returning anything that the next then block can use.
you could do:
Mail.findById(id)
.exec()
.then((mail) => {
if (!mail) {
console.log(mail) // returns null
return res.status(404).send() //need to send response;
}
Mail.deleteOne({ _id: id })
.exec()
.then(() => {
res.status(200).json({
message: 'Mail deleted',
});
})
}).catch((err) => {
res.status(500).json({ error: err });
})
PRO TIP: if you don't know it, learn async await. Code will look much cleaner!
Then it would look like this:
router.delete('/:id', async (req, res) => {
const { id } = req.params;
try {
const mail = await Mail.findById(id);
if(!mail) {
return res.status(404).send();
}
await Mail.deleteOne({_id: id});
res.status(200).json({
message: 'Mail deleted',
});
} catch(e) {
res.status(500).json({ error: err });
}
I've looked through the other issues with bcrypt.compare on GitHub and none of the solutions have worked for me. It always fails on console.log("failed 3") inside bcrypt.compare()
I've tried switching the .then() instead of using a callback with bcrypt.compare as suggested by another post but that hasn't help. Any help would be greatly appreciated!
Below is a copy of my code and summary of versions being used:
Node v8.12.0
Express 4.16.0
bcrypt 3.0.3
jsonwebtoken 8.4.0
mongoose 5.4.1
Bcrypt Hash (Password Hashing)
function saveUserData(req, res, next, userSignUpInfo, info){
bcrypt.hash(req.body.email, 10, (err, hash) =>{
if (err){
return res.status(500).json({
error: err
})
} else {
console.log('inside test route')
console.log('req.body.fnUserName', userSignUpInfo.fnUserName)
const userData = new UserData({
fnUserName : userSignUpInfo.fnUserName,
password : hash,
email : req.body.email,
verify: userSignUpInfo.verify,
createAccountDate: userSignUpInfo.createAccountDate,
userId : userSignUpInfo.userId,
friends: null,
online: null
})
userData.save()
.then(result => {
console.log('result from MongoDB Cloud', result);
saveApiData(info, userSignUpInfo, res);
})
.catch(err => console.log('error from MongoDB Cloud', err));
}
})
}
Bcrypt Compare (Auth User)
router.post('/login', (req, res, next) => {
UserData.find({email: req.body.email})
.exec()
.then(user => {
if(user.length < 1) {
console.log("failed 1")
return res.status(401).json({
message: 'Authentication Failed'
});
}
console.log('user[0].password', user[0].password)
console.log(' user[0].password', user[0].password)
console.log(' req.body.password', req.body.password)
bcrypt.compare(req.body.password,user[0].password).then(function(err, result) {
if (err) {
console.log("failed 1")
return res.status(401).json({
message: 'Authentication Failed'
});
}
if (result) {
const token = jwt.sign(
{
email: user[0].email,
userId: user[0].userId
},
process.env.JWT_KEY,
{
expiresIn: "1h" // he suggested one hour
}
);
console.log("failed 2")
return res.status(200).json({
message: 'Authentication Successful',
token: token
})
} else {
console.log("failed 3")
res.status(401).json({
message: 'Authentication Failed'
})
}
})
})
.catch(err => {
console.log('err in login', err);
res.status(500).json({
error: err,
message: 'error logging in'
})
})
});
Usually, password is saved as hash in the database. Also, provide adequate length for saving hashes into database.(atleast 60 varchars). To do so,
schema.pre("save", function (next) {
bcrypt.hash(this.password, 10, (err, hash) => {
this.password = hash;
next();
});
});
Then, plain password is compared against the hash from database.
bcrypt.hash('mypassword', 10, function(err, hash) {
if (err) { throw (err); }
bcrypt.compare('mypassword', hash, function(err, result) {
if (err) { throw (err); }
console.log(result);
});
});
result will always be undefined since promises return a single value and errors are simply thrown in the catch phrase. So basically, in your code, err will contain the actual result.
What your code should look like is the following:
bcrypt.compare(req.body.password,user[0].password).then((result)=>{
if(result){
console.log("authentication successful")
// do stuff
} else {
console.log("authentication failed. Password doesn't match")
// do other stuff
}
})
.catch((err)=>console.error(err))
It looks like you don't return your res.status inside the
else { console.log("failed 3")
block like you do in the failed 2 and failed 1 blocks.