Express页面无法读取MongoDB对象中部分字段值的问题
我之前做Express+Handlebars项目时,完完全全踩过一模一样的坑:把Mongoose的User实例传到模板后,打印整个{{user}}能看到verified、vericode这些字段,但单独访问{{user.verified}}就是空的,其他字段比如username、phone却能正常渲染。数据库里数据明明是对的,用Users.update()修改后页面也能同步,就是单独读这俩字段不行。
问题根源
这本质是Mongoose Schema的定义问题:Mongoose的文档实例(就是你从数据库查出来的user对象)会严格按照Schema的定义生成可访问的属性。如果Schema里没声明某个字段,或者声明时加了特殊限制,即使数据库里有这个字段,你也没法直接通过user.fieldName访问到——而{{user}}能显示是因为Mongoose的toString()方法会把数据库里所有字段都输出出来,和实例本身的可枚举属性不是一回事。
解决步骤
1. 检查Schema是否定义了目标字段
首先去你的User Schema代码里,确认verified和vericode是不是已经正确添加了:
const mongoose = require('mongoose'); const userSchema = new mongoose.Schema({ username: { type: String, required: true }, phone: { type: String, required: true }, // 重点:必须在Schema里明确声明这两个字段 verified: { type: Boolean, default: false }, vericode: { type: String } }); module.exports = mongoose.model('User', userSchema);
如果之前漏了这两个字段的定义,补上之后重启服务,再试一次应该就能正常访问了。
2. 排查是否添加了select: false选项
如果Schema里已经定义了这两个字段,但还是读不出来,那大概率是给字段加了select: false——这个选项会让Mongoose在默认查询时不返回该字段:
// 错误示例:加了select: false导致默认查询不返回 verified: { type: Boolean, default: false, select: false }, vericode: { type: String, select: false }
解决方法有两种:
- 直接去掉
select: false选项; - 如果必须保留这个选项,就在查询用户的时候显式指定要返回这两个字段:
// 路由里的查询代码 const user = await User.findById(req.user.id).select('+verified +vericode'); // 注意字段名前的+号,表示强制返回被标记为select: false的字段
3. 验证实例属性是否可访问
你可以在路由里加一行调试代码,确认字段是否能被实例访问:
console.log(user.toObject()); // 转换成普通JS对象,看有没有这两个字段 console.log(user.get('verified')); // 用Mongoose的get()方法直接获取字段值
如果toObject()里有这两个字段,那说明Schema定义没问题,可能是Handlebars的缓存问题——清空模板缓存再试试;如果get()能拿到值但直接访问不行,那就是Schema的select选项在搞鬼。
额外提示
另外还要注意,如果你是用User.create()或者new User()创建用户时没有给verified和vericode赋值,后来才通过数据库直接修改的,那Mongoose的旧实例可能不会自动同步这些未定义的字段——不过你说用Users.update()后页面能同步,这个情况应该不存在,但还是可以排查下。
内容的提问来源于stack exchange,提问作者mrSidX

