我为后端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)
没有停止执行流程?
答案 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()
。