You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建课程时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)
        });
    }
};

额外优化点

  1. 若后续需要在其他中间件(如hasAuthorization)中使用学生ID,建议将student._id挂载到req.studentId而非req.id,避免命名冲突。
  2. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 14:53:16