You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Mongoose预保存中间件会吞掉MongoDB的E11000重复键错误?

问题根源:Pre-save中间件的参数定义错误

兄弟,折腾你3天的问题其实出在pre-save中间件的参数写错了!你看你的中间件代码:

userSchema.pre("save", async function (req, res, next) {
  console.log("USER PRE SAVE HOOK");
  next();
});

Mongoose的pre('save')是模型层面的中间件,它的回调函数只接收一个参数:next(用于触发后续流程的回调)。你错误地添加了req和res参数,这会导致Mongoose传递的next被放到req变量的位置,而你代码里的next变量实际上是undefined。

为什么会出现这种奇怪现象?

当你调用next()时,实际上是在调用一个undefined的值,这会抛出TypeError: next is not a function错误。但因为你的中间件是async函数,这个错误会被包装成一个被拒绝的Promise。

Mongoose在处理中间件时,如果遇到被拒绝的Promise,会直接终止后续的保存流程——也就是说,它根本没把数据发给MongoDB去执行插入操作。这就解释了所有奇怪现象:

  1. 数据库里没有重复数据:因为保存流程提前终止了,根本没触发MongoDB的唯一索引校验
  2. 没有返回E11000错误:因为MongoDB从来没收到插入请求,自然不会抛出重复键错误
  3. 接口返回成功:因为你的控制器代码没有错误捕获逻辑,中间件的错误被Express默认错误处理逻辑掩盖,导致代码继续执行到了res.send。

修复方案

把pre-save中间件的参数改成正确格式即可,有两种常用写法:

写法1:普通函数 + next回调

userSchema.pre("save", function(next) {
  console.log("USER PRE SAVE HOOK");
  next(); // 正确调用next,触发后续保存流程
});

写法2:async函数(无需next)

如果中间件里有异步操作,推荐用async/await语法,此时不需要手动调用next,Mongoose会自动等待async函数完成后继续流程:

userSchema.pre("save", async function() {
  console.log("USER PRE SAVE HOOK");
  // 这里可以写await操作,比如密码加密等逻辑
});

验证步骤

修复后重新测试:

  1. 用email@example.com创建用户,成功写入数据库
  2. 再次用相同邮箱创建用户,Mongoose会正常执行保存流程,MongoDB触发唯一索引校验,抛出E11000重复键错误(建议给控制器加上try/catch逻辑来捕获并返回这个错误)

内容的提问来源于stack exchange,提问作者leonardgable

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 14:52:31