user8331511
user8331511

Reputation:

Can't set headers after they are sent node

I have the following route for the password reset form, the form has a password and password confirmation field.

router.post('/users/reset/:token', (req, res, next) => {
  if(req.body.password === req.body['password-confirm']) {
    req.flash('error', 'Passwords do not match!');
    res.redirect('/users/forgot');
  }

  User.findOne({
    resetPasswordToken: req.params.token,
    resetPasswordExpires: { $gt: Date.now() }
  }, function(err, user) {
    if(!user) {
      req.flash('error', ' Password reset is invalid or has expired');
      res.redirect(302, '/login');
    }

    const setPassword = promisify(user.setPassword, user);
    setPassword(req.body.password);
    user.resetPasswordToken = undefined;
    user.resetPasswordExpires = undefined;
    const updatedUser = user.save();

    user.save((saveError, updatedUser) => {
      // Check if saveError is present here and handle appropriately
      req.login(updatedUser, loginError => {
        req.flash('success_msg', 'Your password has been reset successfully! You are now logged in!');
        res.redirect('/dashboard' + req.user);
      })
    });
  });
});

When I fill out the form I get the following error

Fri Jan 26 2018 11:28:55 GMT+0000 (GMT): GET /users/reset/6e2574bfa532e0d13af7fae61114308f9a683767
Mongoose: users.findOne({ resetPasswordExpires: { '$gt': new Date("Fri, 26 Jan 2018 11:28:55 GMT") }, resetPasswordToken: '6e2574bfa532e0d13af7fae61114308f

9a683767' }, { fields: {} })
Fri Jan 26 2018 11:28:55 GMT+0000 (GMT): GET /favicon.ico
Fri Jan 26 2018 11:29:02 GMT+0000 (GMT): POST /users/reset/6e2574bfa532e0d13af7fae61114308f9a683767
Mongoose: users.findOne({ resetPasswordExpires: { '$gt': new Date("Fri, 26 Jan 2018 11:29:02 GMT") }, resetPasswordToken: '6e2574bfa532e0d13af7fae61114308f
9a683767' }, { fields: {} })
Mongoose: users.update({ _id: ObjectId("5a5c6740b9e210087e098fd6") }, { '$unset': { resetPasswordExpires: 1, resetPasswordToken: 1 } })
Mongoose: users.update({ _id: ObjectId("5a5c6740b9e210087e098fd6") }, { '$unset': { resetPasswordExpires: 1, resetPasswordToken: 1 } })
(node:1451) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'apply' of undefined
    at /Users/benbagley/Code/poetry-out-loud/node_modules/es6-promisify/dist/promisify.js:75:41
    at new Promise (<anonymous>)
    at /Users/benbagley/Code/poetry-out-loud/node_modules/es6-promisify/dist/promisify.js:54:20
    at /Users/benbagley/Code/poetry-out-loud/routes/users.js:321:5
    at model.Query.<anonymous> (/Users/benbagley/Code/poetry-out-loud/node_modules/mongoose/lib/model.js:4056:16)
    at /Users/benbagley/Code/poetry-out-loud/node_modules/kareem/index.js:273:21
    at /Users/benbagley/Code/poetry-out-loud/node_modules/kareem/index.js:131:16
    at process._tickCallback (internal/process/next_tick.js:150:11)
(node:1451) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a c
atch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:1451) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminat
e the Node.js process with a non-zero exit code.
Fri Jan 26 2018 11:29:02 GMT+0000 (GMT): GET /users/forgot
events.js:136
      throw er; // Unhandled 'error' event
      ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at validateHeader (_http_outgoing.js:503:11)
    at ServerResponse.setHeader (_http_outgoing.js:510:3)
    at ServerResponse.header (/Users/benbagley/Code/poetry-out-loud/node_modules/express/lib/response.js:767:10)
    at ServerResponse.location (/Users/benbagley/Code/poetry-out-loud/node_modules/express/lib/response.js:884:15)
    at ServerResponse.redirect (/Users/benbagley/Code/poetry-out-loud/node_modules/express/lib/response.js:922:18)
    at req.login.loginError (/Users/benbagley/Code/poetry-out-loud/routes/users.js:332:13)
    at /Users/benbagley/Code/poetry-out-loud/node_modules/passport/lib/http/request.js:51:48
    at /Users/benbagley/Code/poetry-out-loud/node_modules/passport/lib/sessionmanager.js:16:14
    at pass (/Users/benbagley/Code/poetry-out-loud/node_modules/passport/lib/authenticator.js:297:14)
    at Authenticator.serializeUser (/Users/benbagley/Code/poetry-out-loud/node_modules/passport/lib/authenticator.js:299:5)
    at SessionManager.logIn (/Users/benbagley/Code/poetry-out-loud/node_modules/passport/lib/sessionmanager.js:14:8)
    at IncomingMessage.req.login.req.logIn (/Users/benbagley/Code/poetry-out-loud/node_modules/passport/lib/http/request.js:50:33)
    at user.save (/Users/benbagley/Code/poetry-out-loud/routes/users.js:330:11)
    at /Users/benbagley/Code/poetry-out-loud/node_modules/mongoose/lib/model.js:4056:16
    at /Users/benbagley/Code/poetry-out-loud/node_modules/mongoose/lib/services/model/applyHooks.js:170:20
    at process._tickCallback (internal/process/next_tick.js:150:11)

I'm still new to node so any refactoring tips, or fixes for this would be most appreciated, been stuck on this issue for a few days, not sure what I'm doing wrong.

Thank you.

Upvotes: 1

Views: 1531

Answers (2)

Mukesh Sharma
Mukesh Sharma

Reputation: 9022

You are sending response to client multiple time, that's why facing Cannot set headers after they are sent to the client error.

In your case, your are not returning (exiting) from the function execution when you should have. E.g. when password doesn't match, you are trying to redirect user to /users/forgot, but you are not returning the function there. Hence, code below if condition executes and try to send response back again.

Solution:

router.post('/users/reset/:token', (req, res, next) => {
    if // some condition {
      // some code
      return res.redirect('/users/forgot');
    }

    User.findOne({
       // some code
    }, function(err, user) {
       if(!user) {
            // some code
           return res.redirect(302, '/login');
       }

       // some code 
       user.save((saveError, updatedUser) => {
           req.login(updatedUser, loginError => {
               // some code
               return res.redirect('/dashboard' + req.user);
           })
       });
    });
});

Upvotes: 1

Faraz Sarwar
Faraz Sarwar

Reputation: 238

First thing is that this error occur when node sent response of API call more than one time. Secondly, it is best practice to use return when sending response for example return res.json(<OBJECT>);

In your code you are checking the password like req.body.password === req.body['password-confirm'] it should be like req.body.password !== req.body['password-confirm'] that may the case that causing node to sent multiple response

Upvotes: 2

Related Questions