问题描述
当前有效的方法:
router.post('/',async (req,res) => {
try {
const userExists = await User.findOne({email: req.body.email})
if (userExists) {
return res.status(400).send('User already exists')
}
} catch (error) {
return res.status(500).send('Internal server error')
} // stops here if a user does exist into database
console.log('do next stuff');
...
})
async function checkUserExistence(req,res) {
try {
const userExists = await User.findOne({email: req.body.email});
if (userExists) {
return res.status(400).send('User already exists');
}
} catch (error) {
return res.status(500).send('Internal server error');
}
}
router.post('/',res) => {
checkUserExistence(req,res) // does not stop here if a user does exist into database
console.log('do next stuff');
...
})
这样做,我仍然得到响应用户已经存在,但是代码继续在调用函数中执行,因为return res.status(400).send('User already exists')
行仅适用于被调用的函数。
请问该如何解决?谢谢您的帮助。
解决方法
从我的角度来看,在重构范围内,您需要将业务逻辑移出控制器。与模型相关的功能对请求或响应一无所知,您只需要向它们提供直接需要的数据即可。
checkUserExistence
函数应仅在用户存在或不存在时通知您
function checkUserExistence(body) {
return User.findOne({email: body.email});
}
并且控制器需要通过响应进行管理
router.post('/',async (req,res) => {
let userExists;
try {
userExists = await checkUserExistence(req.body)
} catch (e) {
return res.status(500).send('Internal server error');
}
if (userExists) {
return res.status(400).send('User already exists');
}
})
,
问题是您需要等待Promise
解决,并知道是否设置了res
。
解决方案1-有趣的时光
async function checkUserExistence(req,res) {
let isExsist = false;
try {
const userExists = await User.findOne({email: req.body.email});
if (userExists) {
res.status(400).send('User already exists');
isExsist = true;
}
} catch (error) {
res.status(500).send('Internal server error');
isExsist = true;
}
return isExsist;
}
router.post('/',res) => {
const isExsist = await checkUserExistence(req,res)
if(isExsist){
return res;
}
console.log('do next stuff');
...
})
这将起作用,但是这种方法存在问题。主要是这样的事实:checkUserExistence
控制着req
并返回bool
只是为了承认req
已经知道的事情。
解决方案2-分离关注点
我们可以重构使代码更加简化,并使其具有定义明确的API。
首先,让我们从req
中删除res
和checkUserExistence
。不需要他们。现在我们知道checkUserExistence
应该收到email
并应该引发两种类型的错误(DB错误和USER_ALREADY_EXSIST)。
async function checkUserExistence(email) {
const userExists = await User.findOne({ email: email });
if(userExists) {
throw new Error('ERROR::USER_ALREADY_EXSIST');
}
}
此功能是Business Layer
的一部分。这意味着它必须与Controller Layer
无关。
关于此方法的一个很好的想法是,您现在可以在没有express
的情况下使用此方法。
第2步是将此Business Layer
方法与控制器一起使用。
router.post('/',res) => {
try {
await checkUserExistence(req.body.email);
res.send("not exist"); // or do stuff
} catch (e) {
switch(e.message) {
case "ERROR::USER_ALREADY_EXSIST": res.status(400).send('User already exists'); brake;
default:
res.status(500).send('Internal server error'); brake;
}
}
})
现在我们可以看到HTTP在做什么。请注意,在default
情况下(500),数据库抛出的所有异常都将得到解决。
我建议您深入研究express
中间件错误处理。它可以减少通用错误的代码重复。参见:https://expressjs.com/en/guide/error-handling.html