如何从Mongoose获取干净数据对象并在Express路由中合并
问题
我正在用Express搭配TypeScript开发登录功能,流程是从用户数据库文档中获取googleId,再用这个googleId查询对应的shift记录,最后要把req.user、shift记录和isAuthenticated: true合并成干净的API响应返回。但用Object.assign和扩展运算符合并后,响应里包含了大量Mongoose内部属性(比如$__、_doc),得不到纯净的合并对象。
相关代码:
import ShiftModel from '../../models/Shift.model'; userRouter.get('/get-user', async (req: Request, res: Response, next: NextFunction) => { try { const { googleId }: any = req.user; const shift = await ShiftModel.findOne({ requiredByUserId: googleId }); // 尝试过的合并方式 // const response = Object.assign({} , req.user, shift, { isAuthenticated: true }); let response = { ...req.user, ...shift, ...{ isAuthenticated: true } } console.log(response); res.status(200).json(req.user); } catch (error) { next(error); } });
合并后得到的响应示例:
{ '$__': InternalCache { activePaths: StateMachine { paths: [Object], states: [Object] }, skipId: true }, '$isNew': false, _doc: { _id: new ObjectId("6501bc166242e72ea25448f3"), days: [ 'monday', 'tuesday', 'thursday', 'friday' ], available: true, shiftPattern: 'days', startHour: '08:00', endHour: '17:30', requiredByUserId: '115426204517705247377', dateCreated: 2023-09-13T13:39:57.561Z, __v: 0 }, isAuthenticated: true }
解决方法
问题核心是req.user和shift都是Mongoose的Document实例,并非普通JavaScript对象,直接扩展会带上Mongoose的内部属性。以下是几种获取纯净对象的可行方案:
方案1:用.toObject()转换Mongoose文档
将Mongoose实例转换为普通JS对象后再合并:
userRouter.get('/get-user', async (req: Request, res: Response, next: NextFunction) => { try { const { googleId }: any = req.user; const shift = await ShiftModel.findOne({ requiredByUserId: googleId }); // 转换为普通JS对象,兼容req.user不是Mongoose实例的情况 const userObj = typeof req.user.toObject === 'function' ? req.user.toObject() : req.user; const shiftObj = shift && typeof shift.toObject === 'function' ? shift.toObject() : {}; const response = { ...userObj, ...shiftObj, isAuthenticated: true }; res.status(200).json(response); } catch (error) { next(error); } });
方案2:查询时用.lean()直接获取普通对象
在Mongoose查询语句后添加.lean(),查询结果直接返回纯净的JS对象,无需额外转换:
userRouter.get('/get-user', async (req: Request, res: Response, next: NextFunction) => { try { const { googleId }: any = req.user; // 添加.lean(),直接返回普通JS对象 const shift = await ShiftModel.findOne({ requiredByUserId: googleId }).lean(); // req.user如果是Mongoose实例仍需转换,根据你的认证中间件情况调整 const userObj = typeof req.user.toObject === 'function' ? req.user.toObject() : req.user; const response = { ...userObj, ...(shift || {}), isAuthenticated: true }; res.status(200).json(response); } catch (error) { next(error); } });
方案3:提取_doc属性(不推荐)
Mongoose文档的实际数据存储在_doc属性中,可直接提取,但该方式依赖Mongoose内部实现,后续版本可能变动,谨慎使用:
const userObj = req.user._doc || req.user; const shiftObj = shift?._doc || {}; const response = { ...userObj, ...shiftObj, isAuthenticated: true };
内容的提问来源于stack exchange,提问作者daniel sas
相关产品推荐
相关产品推荐

