如何为Mongoose的schema.pre(/^find/)中间件创建特定调用例外?
解决Mongoose pre(/^find/)中间件在特定查询时的例外问题
你遇到的核心问题是全局的pre(/^find/)中间件过滤掉了active: false的用户,导致登录时无法查询到已标记为非活跃的账号进行重新激活。下面给出两种可行的解决方案:
方案一:给Query添加自定义跳过标志(推荐)
通过给Mongoose的Query对象添加一个自定义标志,让pre(/^find/)中间件根据这个标志决定是否执行过滤逻辑,语义清晰且可复用性强。
修改Schema中间件
userSchema.pre(/^find/, function (next) { // 仅当没有设置_skipActiveFilter标志时,才执行active过滤 if (!this._skipActiveFilter) { this.find({ active: { $ne: false } }); } next(); }); // 给Query原型添加方法,用于设置跳过标志 userSchema.query.skipActiveFilter = function () { this._skipActiveFilter = true; return this; // 支持链式调用 };
修改登录逻辑中的查询
在登录时调用skipActiveFilter()方法,绕过active过滤:
exports.login = catchAsync(async (req, res, next) => { const { email, password } = req.body; if (!email || !password) { return next(new AppError('Please provide email and password', 400)); } // 添加skipActiveFilter()方法,跳过pre中间件的active过滤 const user = await User.findOne({ email }).skipActiveFilter().select('+password'); if (!user || !(await user.correctPassword(password, user.password))) { return next(new AppError('Incorrect email or password', 401)); } // 重新激活账号 await User.findByIdAndUpdate(user.id, { active: true }); createSendToken(user, 200, res); });
方案二:查询时显式包含非活跃用户
如果不想修改Schema中间件,可以在登录查询中直接指定active的范围,覆盖中间件的过滤条件:
exports.login = catchAsync(async (req, res, next) => { const { email, password } = req.body; if (!email || !password) { return next(new AppError('Please provide email and password', 400)); } // 查询时同时包含active为true和false的用户 const user = await User.findOne({ email, active: { $in: [true, false] } }).select('+password'); if (!user || !(await user.correctPassword(password, user.password))) { return next(new AppError('Incorrect email or password', 401)); } await User.findByIdAndUpdate(user.id, { active: true }); createSendToken(user, 200, res); });
方案对比
- 方案一:语义化强,后续其他需要绕过过滤的场景直接调用
skipActiveFilter()即可,低耦合。 - 方案二:快速实现,但查询条件与中间件逻辑耦合,若后续中间件过滤规则变更,需要同步修改查询条件。
内容的提问来源于stack exchange,提问作者axoiutku
相关产品推荐
相关产品推荐

