随着前后端分离的流行,前端相关技术的需求也越来越大。Mongoose 是一个优秀的 Node.js 操作 MongoDB 的库,它提供了大量的有用的功能。其中中间件钩子函数是 Mongoose 中非常重要的一部分,本文将介绍 Mongoose 的中间件钩子函数的使用方法及常见问题的解析。
中间件钩子函数的概念
Mongoose 的中间件钩子函数指的是在执行操作(如保存、更新、删除)前或后执行的一段代码。中间件的作用是在某个事件触发前或触发后,对事件进行拦截和处理。中间件钩子函数一般用于数据验证、密码加密等操作。
中间件钩子函数分为四种类型:
- document 中间件:它在操作 document 时触发。
- query 中间件:它在查询时触发。
- aggregate 中间件:它在调用 aggregate 时触发。
- model 中间件:它在操作 model 时触发。
使用中间件钩子函数
document 中间件
document 中间件有两种类型:pre 和 post。pre 类型的中间件在对 document 进行任何操作之前执行,而 post 类型的中间件在对 document 进行任何操作之后执行。
如下是一个 document 的 pre 中间件的使用方法:
// javascriptcn.com code example const schema = new mongoose.Schema({ name: String, }); // 在保存前进行密码加密 schema.pre('save', function (next) { const user = this; if (!user.isModified('password')) return next(); bcrypt.genSalt(10, (err, salt) => { if (err) return next(err); bcrypt.hash(user.password, salt, (err, hash) => { if (err) return next(err); user.password = hash; next(); }); }); });
上述代码中,我们使用了 bcrypt 库对用户密码进行了加密。在保存之前,我们判断用户的密码是否被修改,如果没有被修改,我们直接进入下一步操作。否则,我们使用 bcrypt.genSalt() 生成随机 salt,然后使用 bcrypt.hash() 将密码与 salt 进行组合并加密,最后将加密后的密码存储到数据库中。
此外,我们还可以为 document 添加 post 类型的中间件,例如:
// javascriptcn.com code example // 在保存后给用户添加默认角色 schema.post('save', function (doc) { User.updateOne( { _id: doc._id }, { $push: { roles: 'user' } }, (err, res) => { if (err) console.log(err); } ); });
上述代码在用户信息保存成功后,将默认角色 user
添加到该用户的 roles 中。
query 中间件
query 中间件作用于所有 Mongoose Query 类型的操作,包括 deleteOne, deleteMany, updateOne, updateMany, findOne, findOneAndUpdate, findOneAndDelete 等。
下面是一个 query 中间件的使用方法:
// 在查询前进行权限验证 schema.pre(/^find/, function (next) { const isPublicQuery = this.find({ public: { $ne: false } }); if (isPublicQuery) return next(); if (this.user.role === 'admin') return next(); this.find({ user: this.user._id }); next(); });
该代码对查询操作进行了拦截。我们首先判断是否为公共查询,若是,则直接执行下一步操作。如果用户是管理员,则同样直接执行下一步操作。否则,我们将查询限制在该用户下。
model 中间件
model 中间件有两种类型:pre 和 post。pre 类型的中间件在对 model 进行任何操作之前执行,而 post 类型的中间件在对 model 进行任何操作之后执行。
以下是一个 model 中间件的使用方法:
// 在操作 model 前进行操作日志记录 schema.pre('updateOne', function (next) { const filter = this.getFilter(); const doc = this._update; Log.create({ user: this.user._id, action: 'update', filter, doc }); next(); });
上述代码中,我们使用了 Log 模型来记录操作日志。在更新模型之前,我们将查询条件和模型数据记录到日志中。
常见问题解析
如何防止中间件死循环?
在使用中间件时,有时候可能会因为代码逻辑问题造成死循环,从而导致程序崩溃。为了防止死循环,我们可以使用中间件钩子函数的两个参数:next 和 done。
next 表示执行下一个中间件函数,done 表示中断执行。使用 next 和 done 两个参数可以有效地解决中间件死循环的问题。例如:
// javascriptcn.com code example schema.pre('findOneAndUpdate', function (next) { if (this._update.password) { bcrypt.genSalt(10, (err, salt) => { if (err) return next(err); bcrypt.hash(this._update.password, salt, (err, hash) => { if (err) return next(err); if (hash) { this._update.password = hash; next(); } else { done(); } }); }); } else { next(); } });
上述代码中,我们在更新密码前对密码进行了加密,如果密码存在,则进行加密操作;否则,我们使用 done() 将中间件的执行终止。
中间件的执行顺序?
在 Mongoose 中,中间件是按照注册顺序依次执行的。pre 中间件是按照注册顺序从前往后执行,而 post 中间件是按照注册顺序从后往前执行。
结论
中间件钩子函数是 Mongoose 库中非常重要的一部分,它可以有效地实现代码的重用和拦截。本文介绍了 Mongoose 的中间件钩子函数的使用方法及常见问题的解析,从而帮助读者更好地理解和应用中间件钩子函数。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/672e13d4eedcc8a97c872d0c