Node.js Sequelize关联User和Token表时setUser方法报错如何解决
问题原因
你报错的核心原因是关联方法的使用逻辑完全搞反了,且调用了不存在的实例方法:
按照你定义的 User.hasMany(Token)、Token.belongsTo(User) 关联规则,Sequelize 自动生成的关联方法对应关系如下:
User实例的关联方法为addToken、setTokens、getTokens等,作用是给用户绑定关联的 Token 记录Token实例的关联方法为setUser、getUser等,作用是给 Token 记录绑定所属的用户
你在 generateToken 方法中,this 指代的是当前 User 实例,这个实例上根本没有 setUser 方法,且你传入的参数是 JWT 字符串,完全不符合关联方法的参数要求,自然会抛出错误。
修复方案
你已经写了 addToken 方法实现关联,直接删掉多余的错误 setUser 调用即可,修复后的 generateToken 方法如下:
User.prototype.generateToken = async function generateToken() { const jwtid = await nanoid() const token = jwt.sign({ sub: this.id }, { jwtid }) const userToken = await Token.create({ jti: jwtid }) // 仅保留addToken调用即可,该方法会自动给Token表的userId字段赋值完成关联 await this.addToken(userToken) return token }
如果你想用 setUser 方法实现关联,正确写法是在 Token 实例上调用,传入当前用户实例:
User.prototype.generateToken = async function generateToken() { const jwtid = await nanoid() const token = jwt.sign({ sub: this.id }, { jwtid }) const userToken = await Token.create({ jti: jwtid }) // 用Token实例的setUser方法绑定当前用户 await userToken.setUser(this) return token }
更简化的写法是创建 Token 时直接传入 userId,不需要额外调用关联方法:
User.prototype.generateToken = async function generateToken() { const jwtid = await nanoid() const token = jwt.sign({ sub: this.id }, { jwtid }) // 创建时直接赋值userId,一步完成关联 await Token.create({ jti: jwtid, userId: this.id }) return token }
优化建议
建议把关联定义代码移到模型定义之后、实例方法定义之前,逻辑更清晰,也能避免出现关联方法未生成的问题,调整后的完整代码如下:
const { nanoid } = require('nanoid/async') const argon2 = require('argon2') const jwt = require('../lib/jwt') module.exports = (sequelize, Sequelize, Token, Task) => { const User = sequelize.define("users", { age: { type: Sequelize.INTEGER }, name: { type: Sequelize.STRING }, email: { type: Sequelize.STRING }, password: { type: Sequelize.STRING } }, { defaultScope: { attributes: { exclude: ['password'] }, }, scopes: { withPassword: { attributes: {} } } }); // 先定义所有关联关系 User.hasMany(Token) Token.belongsTo(User) User.hasMany(Task) Task.belongsTo(User) // 再定义生命周期钩子 User.beforeCreate(async (user, options) => { const hashedPassword = await argon2.hash(user.password) user.password = hashedPassword }) // 最后定义实例方法 User.prototype.generateToken = async function generateToken() { const jwtid = await nanoid() const token = jwt.sign({ sub: this.id }, { jwtid }) await Token.create({ jti: jwtid, userId: this.id }) return token } User.prototype.verifyPassword = async function verifyPassword(password) { console.log('verify Password instance method', { password, hash: this.password }) return argon2.verify(this.password, password) } return User; };
内容的提问来源于stack exchange,提问作者AskAnyWay
相关产品推荐
相关产品推荐

