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

Node.js中JWT登录报TypeError: Cannot read properties of null (reading '_id')求助

问题

学习JsonWebToken基础时,在Node.js中编写登录接口遇到两个问题:

  • 直接访问doc._id时报错:TypeError: Cannot read properties of null (reading '_id')
  • 移除doc._id后能生成令牌,但返回的data.user为null

已在MongoDB中创建包含_id、name、age、username字段的用户集合,相关代码如下:

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');

const app = express();

app.use(bodyParser.json());

const userSchema = new mongoose.Schema({
    name: { type: String, required: true },
    age: { type: Number, required: true },
    username: { type: String, required: true }, 
});

const User = mongoose.model('User', userSchema);

function login(req, res){ 
User.findOne({  username })
    .then(function(doc){ 
        const token = jwt.sign({ doc._id }, 'abc123', {expiresIn: 250});
        return res.status(200).json({status: 'success', token, data: {doc}}); 
    });                     
}

app.post('/login', login);

错误原因分析

  • 未获取请求体中的username参数:User.findOne({ username })里的username未定义,需从req.body中获取前端传入的用户名,否则查询条件无效,返回的doc为null,访问doc._id必然报错。
  • 缺少查询结果校验逻辑:没有判断是否找到用户,直接对doc进行操作,当用户不存在时就会触发null的属性访问错误。
  • 响应数据键名不匹配:代码返回data: {doc},但你期望的是data.user,键名不一致导致前端拿到的user为null。

修复后的代码

const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');

const app = express();

app.use(bodyParser.json());

// 补充数据库连接逻辑
mongoose.connect('mongodb://localhost:27017/你的数据库名')
  .then(() => console.log('数据库连接成功'))
  .catch(err => console.error('数据库连接失败:', err));

const userSchema = new mongoose.Schema({
    name: { type: String, required: true },
    age: { type: Number, required: true },
    username: { type: String, required: true }, 
});

const User = mongoose.model('User', userSchema);

function login(req, res){ 
  // 从请求体获取用户名
  const { username } = req.body;
  
  if (!username) {
    return res.status(400).json({ status: 'fail', message: '请传入用户名' });
  }

  User.findOne({ username })
    .then(function(doc){ 
      // 判断是否找到用户
      if (!doc) {
        return res.status(404).json({ status: 'fail', message: '用户不存在' });
      }
      
      // 生成JWT,将_id命名为更清晰的userId
      const token = jwt.sign({ userId: doc._id }, 'abc123', { expiresIn: '250s' });
      // 返回符合预期的data.user结构
      return res.status(200).json({ status: 'success', token, data: { user: doc } }); 
    })
    .catch(function(err) {
      // 处理数据库查询错误
      return res.status(500).json({ status: 'error', message: '服务器内部错误', error: err.message });
    });                     
}

app.post('/login', login);

// 启动服务
app.listen(3000, () => console.log('服务运行在http://localhost:3000'));

补充说明

  1. 必须补充MongoDB连接代码,否则无法正常查询数据库;
  2. expiresIn参数传入数字时单位为秒,也可通过字符串指定单位(如'1h'表示1小时);
  3. 生产环境不要将JWT密钥('abc123')硬编码,建议用环境变量存储;
  4. 实际登录逻辑通常还需校验密码,此处为最简实践暂保留仅查询用户名的逻辑。

内容的提问来源于stack exchange,提问作者Miriyan Asenov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 21:18:22