NodeJS - Cannot set Headers after they are sent to

2019-08-24 11:13发布

问题:

So I've searched around and found out that to fix the said issue, I have to return after sending a response. But my problem is, even though I have return, I still have the error.

const dbEditCourse = (req, res, db, logger) => {
    let {
        origCourse, code, description, type
    } = req.body;
    if (!code || !description || !type) {
        res.json({
            haveEmpty: true
        });
        return;
    }
    db.transaction((trx) => {
            db.select('*').from('course_strand').where('code', '=', code)
                .then(data => {
                    if (data[0]) {

                        //error happens in this block of code
                        res.json({
                            isSuccess: false
                        });
                        return;
                        //i also tried return res.json({ isSuccess: false });

                    }
                    //wrapping this in 'else' also does not work
                    return db('course_strand')
                        .returning('*')
                        .where('code', '=', origCourse)
                        .update({ code, description, type })
                })
                .then(course => {
                    return db('activity_logs')
                        .returning('*')
                        .insert({
                            date: new Date(),
                            employee_id: req.session.emp_id,
                            module: "COURSE / STRAND",
                            activity: "EDIT"
                        })
                })
                .then(activity => {
                    if (activity[0]) {
                        res.json({
                            isSuccess: true
                        });
                        return;
                    } else {
                        res.json({
                            isSuccess: false
                        });
                        return;
                    }
                })
                .then(trx.commit)
                .catch(err => {
                    logger.error(err);
                    trx.rollback;
                    res.render('pages/error-500');
                });
        })
        .catch(err => logger.error(err));
}

module.exports = {
    dbEditCourse
}

What I'm doing to produce the error is, If the record is existing, it will go into the block of code above. Aside from that specific block of code, I don't encounter the error elsewhere. And the code is working fine even though I have the error.

回答1:

You cannot break a promise chain with return keyword, all .then statements will be executed (exclude you throw an error in a .then), the res.json has been called many times.

Handler all errors (include your error and system error) in catch block.

In catch block, check the error is throwing by you or not to return the response.

const dbEditCourse = (req, res, db, logger) => {
  let {
    origCourse, code, description, type
  } = req.body;
  if (!code || !description || !type) {
    res.json({
      haveEmpty: true
    });
    return;
  }

  // util throw a error
  const breakWithMyError = () => {
    throw new Error("MY_ERROR");
  }

  db.transaction((trx) => {
    db.select('*').from('course_strand').where('code', '=', code)
      .then(data => {
        if (data[0]) {

          //error happens in this block of code
          breakWithMyError();
          //i also tried return res.json({ isSuccess: false });

        }
        //wrapping this in 'else' also does not work
        return db('course_strand')
          .returning('*')
          .where('code', '=', origCourse)
          .update({ code, description, type })
      })
      .then(course => {
        return db('activity_logs')
          .returning('*')
          .insert({
            date: new Date(),
            employee_id: req.session.emp_id,
            module: "COURSE / STRAND",
            activity: "EDIT"
          })
      })
      .then(activity => {
        // revert logic, we check for error case first
        if (!activity[0]) {
          breakWithMyError();
        }
      })
      .then(trx.commit)
      .then(() => {
        // finally you can run to here without any error
        res.json({
          isSuccess: true
        });
      })
      .catch(err => {
        // If you any error, the error comes form `breakWithMyError` or any things.
        if (err.message === "MY_ERROR") {
          // the error throw by `breakWithMyError`
          return res.json({
            isSuccess: false
          });
        }
        logger.error(err);
        trx.rollback;
        // Why you return a html page in failed case? `res.status(500).json({message: "Internal server!"});`
        res.render('pages/error-500');
      });
  })
    .catch(err => logger.error(err));
}

module.exports = {
  dbEditCourse
}


回答2:

const dbEditCourse = (req, res, db, logger) => {
    let {
        origCourse, code, description, type
    } = req.body;
    if (!(code && description && type)) {
        res.json({
            haveEmpty: true
        });
        return;
    } else { // Please Try this. 
        db.transaction((trx) => {
            db.select('*').from('course_strand').where('code', '=', code)
                .then(data => {
                    if (data[0]) {

                        //error happens in this block of code
                        res.json({
                            isSuccess: false
                        });
                        return;
                        //i also tried return res.json({ isSuccess: false });

                    }
                    //wrapping this in 'else' also does not work
                    return db('course_strand')
                        .returning('*')
                        .where('code', '=', origCourse)
                        .update({ code, description, type });
                })
                .then(course => {
                    return db('activity_logs')
                        .returning('*')
                        .insert({
                            date: new Date(),
                            employee_id: req.session.emp_id,
                            module: "COURSE / STRAND",
                            activity: "EDIT"
                        });
                })
                .then(activity => {
                    if (activity[0]) {
                        res.json({
                            isSuccess: true
                        });
                        return;
                    } else {
                        res.json({
                            isSuccess: false
                        });
                        return;
                    }
                })
                .then(trx.commit)
                .catch(err => {
                    logger.error(err);
                    trx.rollback;
                    res.render('pages/error-500');
                });
        })
        .catch(err => logger.error(err));
    }

};

module.exports = {
    dbEditCourse
};