Node.js操作MongoDB报错user.save is not a function解决方法
报错原因
- 核心触发原因:你当前封装的
db.findOne()方法返回的是从MongoDB读取到的普通JavaScript对象,不是Mongoose等ODM库的文档实例,普通对象本身没有挂载save()实例方法,调用时自然会抛出user.save() is not a function错误。 - 附带逻辑隐患:
- 代码先判断
if(user.accessToken!=''),之后才判断if(!user),如果findOne没查到匹配用户,user值为null,访问null.accessToken会直接触发空指针报错,根本走不到后续的空值判断分支。 - 开头
let user = await db.aggregate('USER', query);属于冗余无效代码:query变量未定义,且后续马上重新给user赋值为findOne的查询结果,这行执行本身就存在报错风险。
- 代码先判断
修复方案
- 调整判断顺序:先校验用户是否存在,再处理旧token失效逻辑,避免空指针异常。
- 放弃调用
save()方法,统一使用你自己封装的db层静态更新方法(和代码中已使用的findOneAndUpdate、create保持同一套调用逻辑),直接通过更新语句将旧token置空,不需要先查询文档再调用实例方法保存,同时还能减少一次数据库交互。 - 删除开头冗余的aggregate无效代码。
- 调整响应返回顺序:等所有数据库更新操作完成后再返回接口响应,避免响应发出后更新操作执行失败的一致性问题。
修复后的完整可运行代码如下:
export const signin = async (req, res) => { try { const code = req.body.code; console.log(code + "code"); const profile = await verifyIdToken(code); console.log(profile + "signinuser"); const newUser = { uid: profile.uid, name: profile.name, email: profile.email, paymentUpdated: false, isLoggedIn: true, userBrowser: req.headers["user-agent"] }; // 删除冗余的aggregate查询,直接查询目标用户 let user = await db.findOne('USER', {email: newUser.email}); // 先判断用户存在,再处理旧token失效逻辑 if (user) { if (user.accessToken !== '') { // 复用已封装的findOneAndUpdate方法置空旧token,无需调用save() await db.findOneAndUpdate('USER', {email: newUser.email}, { $set: { accessToken: '', refreshToken: '' } }); console.log("旧token已失效"); } } else { // 查无对应用户时创建新用户 user = await db.create('USER', newUser); } const token = generateToken(user); // 更新用户accessToken为新生成的有效token await db.findOneAndUpdate('USER', { email: newUser.email }, {$set:{accessToken: token}}) // 所有数据库操作完成后返回响应 res.status(200).json({ user, token }); } catch (err) { console.log(err) res.status(500).json({ message: "Something went wrong" }); } };
补充说明:如果你后续直接使用原生Mongoose的
findOne方法且需要调用save(),请确认查询时没有添加.lean()选项——添加.lean()后返回的结果就是普通JS对象,不会携带save()等实例方法。从你当前db.xxx方法第一个参数传集合名字符串的调用形式来看,你是自行封装了一层数据库操作,默认返回普通对象,直接使用封装好的更新方法是最稳妥的方案。
内容的提问来源于stack exchange,提问作者user18246964
相关产品推荐
相关产品推荐

