5月29日 01:08

Mongoose 中间件的 pre 和 post 钩子怎么用?

Mongoose 中间件分两类:文档中间件(作用于 save/validate/removethis 指向文档实例)和查询中间件(作用于 find/findOne/updateOne/deleteOnethis 指向 Query 对象)。pre 钩子在操作前执行,可修改数据或通过 next(err) 中止操作;post 钩子在操作后执行,接收结果参数,不能中止但适合日志和通知。异步中间件用 async function 即可,不需要手动调 next(),抛异常自动中止。经典场景:pre save 哈希密码、post save 写审计日志、pre find 过滤软删除记录。注意 findOneAndUpdate 不触发 save 钩子,需要单独注册 findOneAndUpdate 的 pre/post。

追问

pre 钩子中 next() 和 async/await 能混用吗? 不建议。选一种即可:传统写法调 next(err),async 写法直接抛异常或正常返回。混用可能导致钩子执行两次或跳过后续中间件。

查询中间件里怎么修改查询条件? 通过 this(Query 对象)调用 .where().skip() 等方法,例如 this.where({ deleted: false })。不要直接修改 this._conditions,应使用 Query API。

post 钩子能拿到修改前的值吗? post savethis 是保存后的文档,doc 参数也是。如需旧值,在 pre save 中用 this.modifiedPaths() 记录变更字段,或用 this.$__original() 在 Mongoose 7+ 获取原始值。

remove 中间件在 Mongoose 7+ 有什么变化? remove 钩子已废弃,改为 deleteOne。文档级的 this.deleteOne() 触发文档中间件,Model.deleteOne() 触发查询中间件,两者 this 含义不同。

写段代码

javascript
userSchema.pre('save', async function() { if (!this.isModified('password')) return; this.password = await bcrypt.hash(this.password, 10); }); userSchema.post('save', function(doc) { AuditLog.create({ action: 'save', docId: doc._id }); });
标签:Mongoose