创建课程时TypeError: Cannot read properties of null (reading '_id')问题排查
问题分析与解决方案
问题描述
使用Mongoose开发系统时,登录、登出功能正常,但创建课程时出现TypeError: Cannot read properties of null (reading '_id')错误。控制台打印student._id显示正常,但保存课程时提示id为null。通过ref关联Student与Course文档,相关代码如下:
Model.js
const mongoose = require('mongoose'); const Schema = mongoose.Schema; var CourseSchema = new Schema({ courseCode: { type: String, required: true, unique: true, }, courseName: { type: String, required: 'Course Name cannot be blank', }, section: { type: String, required: 'Section cannot be blank', }, semester: { type: String, required: 'Semester cannot be blank', }, creator: { type: Schema.ObjectId, ref: 'Student' }, created: { type: Date, default: Date.now } }); mongoose.model('Course', CourseSchema);
Controller.js
const mongoose = require('mongoose'); const Course = mongoose.model('Course'); const Student = require('mongoose').model('Student'); function getErrorMessage(err) { if (err.errors) { for (let errName in err.errors) { if (err.errors[errName].message) return err.errors[errName].message; } } else { return 'Unknown server error'; } }; exports.create = function (req, res) { const course = new Course(); course.courseCode = req.body.courseCode; course.courseName = req.body.courseName; course.section = req.body.section; course.semester = req.body.semester; console.log(req.body) Student.findOne({studentNumber: req.body.studentNumber}, (err, student) => { if (err) { return getErrorMessage(err); } if (!student) { return res.status(400).send({ message: 'No student found with the given student number' }); } console.log('student._id',student._id); req.id = student._id; console.log('student._id',req.id); }).then( function () { course.creator = req.id console.log('req.student._id',req.id); course.save((err) => { if (err) { console.log('error', getErrorMessage(err)) return res.status(400).send({ message: getErrorMessage(err) }); } else { res.status(200).json(course); } }); }); }; // 其余方法省略...
问题原因
核心问题是混用回调函数与Promise语法:
- 调用
Student.findOne()时传入了回调函数,此时Mongoose不会返回Promise对象,后续的.then()会被立即执行。 - 回调函数是异步执行的,
.then()执行时,回调里的req.id = student._id还未完成赋值,导致req.id为null,最终course.creator被设为null,触发保存时的关联错误。
解决方法
统一使用async/await语法重构create方法,确保获取到student对象后再执行课程保存逻辑:
修复后的create函数
exports.create = async function (req, res) { try { const course = new Course(); course.courseCode = req.body.courseCode; course.courseName = req.body.courseName; course.section = req.body.section; course.semester = req.body.semester; console.log(req.body); // 使用await同步获取student对象 const student = await Student.findOne({ studentNumber: req.body.studentNumber }); if (!student) { return res.status(400).send({ message: 'No student found with the given student number' }); } console.log('student._id', student._id); course.creator = student._id; // 直接赋值,无需挂载到req对象 // 保存课程 await course.save(); res.status(200).json(course); } catch (err) { console.log('error', getErrorMessage(err)); return res.status(400).send({ message: getErrorMessage(err) }); } };
额外优化点
- 若后续需要在其他中间件(如
hasAuthorization)中使用学生ID,建议将student._id挂载到req.studentId而非req.id,避免命名冲突。 - 在
hasAuthorization中,ObjectId与字符串比较时需转换为字符串:
exports.hasAuthorization = function (req, res, next) { console.log('in hasAuthorization - creator: ',req.course.creator) console.log('in hasAuthorization - student: ',req.studentId) // ObjectId转字符串后再比较 if (req.course.creator.toString() !== req.studentId) { return res.status(403).send({ message: 'Student is not authorized' }); } next(); };
内容的提问来源于stack exchange,提问作者beginner
相关产品推荐
相关产品推荐

