next(错误)不停止执行

时间:2017-07-08 22:49:37

标签: node.js express error-handling

我为后端API创建了一个身份验证路由:

const express = require("express");
const jwt = require("jsonwebtoken");

const User = require("../models/User");

let router = express.Router();

router.post("/", (req, res, next) => {
  const { username, phone, password } = req.body;
  if (!(username || phone) || !password) {
    let err = new Error("invalid parameters");
    err.status = 400;
    next(err);
  }
  // XXX: Perhaps a better way to do this
  let params = {};
  if (username) {
    params.username = username;
  }
  if (phone) {
    params.phone = phone;
  }
  User.findOne(params)
    .then(user => {
      if (!user) {
        let err = new Error("invalid credentials");
        err.status = 401;
        next(err);
      }
      user.checkPassword(password, (err, isMatch) => {
        if (err) {
          next(err);
        }

        if (!isMatch) {
          console.log("we get here");
          let err = new Error("invalid credentials");
          err.status = 401;
          next(err);
        }
        console.log("we also get here");
        res.send({
          token: jwt.sign(
            {
              _id: user._id,
              username: user.username,
              phone: user.phone
            },
            req.app.get("jwtSecret")
          )
        });
      });
    })
    .catch(err => {
      next(err);
    });
});

module.exports = router;

当传入有效的用户名但密码无效时,我得到输出:

we got here
we also got here
Error: Can't set headers after they are sent.
    at ...

我认为错误是因为next(err)没有停止执行流程,因此响应被发送两次。

为什么next(err)没有停止执行流程?

1 个答案:

答案 0 :(得分:3)

致电return后,您需要next(err)

next(err)会停止将来的路由,但它不会在您自己的函数中停止执行。因此,您需要使用if / else或者在完成后停止执行以阻止自己函数的其他部分执行。

就个人而言,我会使用promises进行所有asnyc操作,而不是使用promises和callbacks的混合搭配。然后,您可以拒绝并将所有内容汇总到最后一个.catch()

但是,如果你要坚持你的承诺和回调,你可以添加return这样的陈述:

router.post("/", (req, res, next) => {
    const { username, phone, password } = req.body;
    if (!(username || phone) || !password) {
        let err = new Error("invalid parameters");
        err.status = 400;
        next(err);
        return;
    }
    // XXX: Perhaps a better way to do this
    let params = {};
    if (username) {
        params.username = username;
    }
    if (phone) {
        params.phone = phone;
    }
    User.findOne(params).then(user => {
        if (!user) {
            let err = new Error("invalid credentials");
            err.status = 401;
            throw err;
        }
        user.checkPassword(password, (err, isMatch) => {
            if (err) {
                next(err);
                return;
            }

            if (!isMatch) {
                console.log("we get here");
                let err = new Error("invalid credentials");
                err.status = 401;
                next(err);
                return;
            }
            console.log("we also get here");
            let token = jwt.sign({_id: user._id, username: user.username, phone: user.phone}, req.app.get("jwtSecret"))
            res.send({token});
        });
    }).catch(err => {
        next(err);
    });
});

如果您更改user.checkPassword()的实现以返回承诺而不是使用回调,那么您可以这样做而不混合回调和承诺:

router.post("/", (req, res, next) => {
    function throwErr(msg, status) {
        let err = new Error(msg);
        err.status = status;
        throw err;
    }

    Promise.resolve().then(() => {
        const { username, phone, password } = req.body;
        if (!(username || phone) || !password) {
            throwErr("invalid parameters", 400);
        }
        let params = {};
        if (username) {
            params.username = username;
        }
        if (phone) {
            params.phone = phone;
        }
        return User.findOne(params).then(user => {
            if (!user) {
                throwErr("invalid credentials", 401);
            }
            return user.checkPassword(password).then(isMatch) => {
                if (!isMatch) {
                    throwErr("invalid credentials", 401);
                }
                let token = jwt.sign({_id: user._id, username: user.username, phone: user.phone}, req.app.get("jwtSecret"))
                res.send({token});
            });
        });
    }).catch(err => {
        next(err);
    });
});

throwErr()来电将全部以.catch()

结束