在记录用户时发送标题后无法设置标题

时间:2018-01-05 20:45:33

标签: javascript express bcrypt

我在这里挠头,我使用bcrypt尝试登录用户,但我收到以下错误:

Error: Can't set headers after they are sent.

这是该路线的代码:

router.post('/login', (req, res, next) => {
  User.find({ email: req.body.email })
    .exec()
    .then(user => {
      if (user.length < 1) {
        res.status(401).json({
          message: 'Auth failed'
        });
      }
      bcrypt.compare(req.body.password, user[0].password, (err, result) => {
        if (err) {
          res.status(401).json({
            message: 'Auth failed'
          });
        }
        if (result) {
          res.json(200).json({
            message: 'Auth successful'
          });
        }
        return res.status(401).json({
          message: 'Auth failed 3'
        });
      });
    })
    .catch(err => {
      res.status(500).json({
        error: err
      });
    });
});

我认为错误可能来自if else语句,并尝试两次发送标题,但是我会在继续下一个条件号之前结束它们?我想念一下这里的东西吗?

1 个答案:

答案 0 :(得分:1)

当您的代码尝试为同一请求发送多个响应时,会看到您看到的错误。此错误的典型原因是您处理异步操作的编码错误。在您显示的代码中,我可以看到以下可能导致此错误的错误:

  1. 如果user.length < 1,则执行res.status(401).json(...),然后让代码继续运行,然后发送其他回复。

  2. 如果您从bcrypt.compare()收到错误,则会发送错误响应,然后让代码继续运行并发送其他回复。

  3. 如果bcrypt.compare()成功,则发送res.json(200).json(...),这是错误的。你可能意味着res.status(200).json(...)

  4. 如果bcrypt.compare()成功并且您有result,则会发送两个回复。

  5. 在查看代码时,您似乎认为只要执行res.json()函数返回并且没有执行其他代码。事实并非如此。在你点击return之前,该函数中的其余代码将继续执行。

    以下是修复它的一种方法(添加一些return语句和一个else):

    router.post('/login', (req, res, next) => {
        User.find({ email: req.body.email }).exec().then(user => {
            if (user.length < 1) {
              res.status(401).json({message: 'Auth failed'});
              return;
            }
            bcrypt.compare(req.body.password, user[0].password, (err, result) => {
              if (err) {
                res.status(401).json({message: 'Auth failed'});
                return;
              }
              if (result) {
                res.json({message: 'Auth successful'});
              } else {
                res.status(401).json({message: 'Auth failed 3'});
              }
            });
        }).catch(err => {
            res.status(500).json({error: err});
        });
    });
    

    或者,为了以更清洁的方式执行此操作,其中所有具有相同状态的响应被组合并且所有流控制都使用promises完成,您可以执行以下操作:

    const util = require('util');
    bcyrpt.compareAsync = util.promisify(bcrypt.compare);
    
    router.post('/login', (req, res, next) => {
        User.find({ email: req.body.email }).exec().then(user => {
            if (user.length < 1) {
                throw new Error('No user match');
            }
            return bcrypt.compareAsync(req.body.password, user[0].password).then(result => 
                if (!result) {
                    throw new Error('Auth failed 2');
                }
                res.json({message: 'Auth successful'});
              }
            }).catch(err => {
                res.json(401).json({message: err.message});
            });
        }).catch(err => {
            res.status(500).json({error: err});
        });
    });