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

使用Sequelize的Model.create创建用户时触发类型错误问题排查

使用Sequelize的Model.create提交用户数据时遇到TypeError: Cannot convert undefined or null to object错误的解决

问题描述

调用Sequelize的Model.create方法创建用户时触发错误:TypeError: Cannot convert undefined or null to object。其他模型的POST、GET路由均正常,已传入nome、login、senha字段(senha_hash通过钩子由senha自动生成),尝试判断传入对象是否为null/undefined但未触发提示,报错依旧。

相关代码文件

迁移文件(usuarios.js)

/** @type {import('sequelize-cli').Migration} */
module.exports = {
  up: (queryInterface, Sequelize) => queryInterface.createTable('usuarios', {
    id: {
      type: Sequelize.INTEGER,
      autoIncrement: true,
      allowNull: false,
      primaryKey: true,
    },
    nome: {
      type: Sequelize.STRING,
      allowNull: false,
    },
    login: {
      type: Sequelize.STRING,
      allowNull: false,
      unique: true,
    },
    senha_hash: {
      type: Sequelize.STRING,
      allowNull: false,
    },
    created_at: {
      type: Sequelize.DATE,
    },
    updated_at: {
      type: Sequelize.DATE,
    },
  }),

  down: (queryInterface) => queryInterface.dropTable('usuarios'),
};

模型文件(Usuario.js)

import Sequelize, { Model } from 'sequelize';
import bcryptjs from 'bcryptjs';

export default class Aluno extends Model {
  static init(sequelize) {
    super.init({
      nome: {
        type: Sequelize.STRING,
        defaultValue: '',
        validate: {
          len: {
            args: [3, 255],
            msg: 'O nome deve conter de 3 a 255 caracteres.',
          },
        },
      },
      login: {
        type: Sequelize.STRING,
        defaultValue: '',
        unique: {
          msg: 'Este nome de usuário já existe.',
        },
      },
      senha_hash: {
        type: Sequelize.STRING,
        defaultValue: '',
      },
      senha: {
        type: Sequelize.VIRTUAL,
        defaultValue: '',
        validate: {
          len: {
            args: [6, 24],
            msg: 'A senha precisa ter entre 6 e 24.',
          },
        },
      },
    }, {
      sequelize,
    });

    this.addHook('beforeSave', (user) => {
      user.senha_hash = bcryptjs.hash(user.senha, 8);
    });

    return this;
  }
}

控制器文件(UsuarioController.js)

import Usuario from '../models/Usuario';

class UsuarioController {
  async index(req, res) {
    const usuarios = await Usuario.findAll();

    return res.status(200).json(usuarios);
  }

  async store(req, res) {
    try {
      const { nome, login, senha } = req.body;

      const dados = { nome, login, senha };

      const novoUsuario = await Usuario.create(dados);

      // if (dados === null || dados === undefined) {
      //   return res.status(400).json('Dados vazios');
      // }
      return res.status(200).json(novoUsuario);
    } catch (e) {
      return null;
    }
  }
}

export default new UsuarioController();

路由文件(usuarioRoutes.js)

import { Router } from 'express';
import usuarioController from '../controllers/UsuarioController';

const router = new Router();

router.post('/', usuarioController.store);
router.get('/', usuarioController.index);

export default router;

主文件(app.js)

import dotenv from 'dotenv';

dotenv.config();

import './src/database';
import express from 'express';
import homeRoutes from './src/routes/homeRoutes';
import alunoRoutes from './src/routes/alunoRoutes';
import instituicaoRoutes from './src/routes/instituicaoRoutes';
import disciplinaRoutes from './src/routes/disciplinaRoutes';
import turmaRoutes from './src/routes/turmaRoutes';
import usuarioRoutes from './src/routes/usuarioRoutes';

class App {
  constructor() {
    this.app = express();
    this.middlewares();
    this.routes();
  }

  middlewares() {
    this.app.use(express.urlencoded({ extended: true }));
    this.app.use(express.json());
  }

  routes() {
    this.app.use('/', homeRoutes);
    this.app.use('/alunos', alunoRoutes);
    this.app.use('/instituicao', instituicaoRoutes);
    this.app.use('/disciplina', disciplinaRoutes);
    this.app.use('/turma', turmaRoutes);
    this.app.use('/usuarios', usuarioRoutes);
  }
}

export default new App().app;

错误原因及解决方法

1. 模型类名定义错误

模型文件中定义的类名为Aluno(意为学生),但导出的文件名为Usuario.js,控制器中导入的也是Usuario。类名与导入名称不匹配,导致Sequelize无法正确识别并初始化用户模型,这是核心错误。

修复:
将模型类名改为Usuario:

export default class Usuario extends Model {
  // ... 原有代码不变
}

2. beforeSave钩子的异步问题与空值处理

bcryptjs.hash是异步方法,直接赋值会导致senha_hash接收的是Promise对象,而非哈希后的字符串;同时如果user.senha为空或未传入,hash方法会接收undefined,触发类型错误。

修复:
改用同步哈希方法hashSync,并增加非空判断:

this.addHook('beforeSave', (user) => {
  if (user.senha) {
    user.senha_hash = bcryptjs.hashSync(user.senha, 8);
  }
});

如果要使用异步版本,需将钩子改为异步函数:

this.addHook('beforeSave', async (user) => {
  if (user.senha) {
    user.senha_hash = await bcryptjs.hash(user.senha, 8);
  }
});

3. 控制器错误处理与参数校验问题

  • 参数校验位置错误:原代码在create之后判断dados是否为null/undefined,但dados是字面量对象,永远不会为null/undefined,应该在create之前校验字段是否存在。
  • 错误捕获后直接返回null,导致客户端收不到任何响应,应该返回错误信息。

修复:

async store(req, res) {
  try {
    const { nome, login, senha } = req.body;

    if (!nome || !login || !senha) {
      return res.status(400).json('Dados vazios');
    }

    const dados = { nome, login, senha };
    const novoUsuario = await Usuario.create(dados);

    return res.status(200).json(novoUsuario);
  } catch (e) {
    return res.status(400).json({ erro: e.message });
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 14:00:55