乐闻世界logo
搜索文章和话题

Mongoose 中间件和钩子如何工作,有哪些应用场景?

2月22日 20:12

Mongoose 中间件(Middleware)和钩子(Hooks)是强大的功能,允许在执行某些操作之前或之后执行自定义逻辑。中间件分为两类:文档中间件和查询中间件。

中间件类型

1. 文档中间件(Document Middleware)

在文档实例上执行的操作,如 save()validate()remove() 等。

javascript
userSchema.pre('save', function(next) { console.log('About to save user:', this.name); next(); }); userSchema.post('save', function(doc) { console.log('User saved:', doc.name); });

2. 查询中间件(Query Middleware)

在 Model 查询上执行的操作,如 find()findOne()updateOne() 等。

javascript
userSchema.pre('find', function() { this.where({ deleted: false }); }); userSchema.post('find', function(docs) { console.log('Found', docs.length, 'users'); });

常用钩子

文档操作钩子

  • validate - 验证文档
  • save - 保存文档
  • remove - 删除文档
  • init - 初始化文档(从数据库加载)

查询操作钩子

  • count - 计数查询
  • find - 查找文档
  • findOne - 查找单个文档
  • findOneAndDelete - 查找并删除
  • findOneAndUpdate - 查找并更新
  • updateOne - 更新单个文档
  • updateMany - 更新多个文档
  • deleteOne - 删除单个文档
  • deleteMany - 删除多个文档

Pre 和 Post 钩子的区别

Pre 钩子

  • 在操作执行前运行
  • 可以修改数据或中止操作
  • 必须调用 next() 或返回 Promise
  • 可以访问 this(文档实例或查询对象)
javascript
userSchema.pre('save', function(next) { if (this.age < 0) { const err = new Error('Age cannot be negative'); return next(err); } this.email = this.email.toLowerCase(); next(); });

Post 钩子

  • 在操作执行后运行
  • 不能修改数据或中止操作
  • 接收操作结果作为参数
  • 可以访问 this(文档实例或查询对象)
javascript
userSchema.post('save', function(doc) { console.log('User saved with ID:', doc._id); // 发送通知、记录日志等 });

异步中间件

Mongoose 中间件支持异步操作:

javascript
// 使用 async/await userSchema.pre('save', async function(next) { const existing = await this.constructor.findOne({ email: this.email }); if (existing && existing._id.toString() !== this._id.toString()) { const err = new Error('Email already exists'); return next(err); } next(); }); // 返回 Promise userSchema.pre('save', function() { return checkEmailAvailability(this.email).then(isAvailable => { if (!isAvailable) { throw new Error('Email already exists'); } }); });

实际应用场景

  1. 密码哈希:在保存用户前对密码进行加密
  2. 时间戳:自动设置 createdAt 和 updatedAt
  3. 软删除:在删除前标记为已删除
  4. 数据验证:执行复杂的验证逻辑
  5. 日志记录:记录操作历史
  6. 缓存失效:更新相关缓存
  7. 关联数据:自动更新关联文档
  8. 通知发送:在操作后发送通知

注意事项

  1. 中间件按定义顺序执行
  2. pre 钩子中的错误会中止操作
  3. 查询中间件不会触发文档中间件
  4. 使用 findOneAndUpdate 等方法时,需要设置 { runValidators: true } 来触发验证
  5. 中间件中避免无限循环
标签:Mongoose