如何捕获MongoDB E11000重复邮箱错误并返回自定义报错
实现方案
你当前代码仅对用户名做了重复校验,未覆盖邮箱重复场景,也未捕获MongoDB唯一索引抛出的E11000错误,才会返回默认的系统报错。两种实现方式可按需选择,建议组合使用覆盖所有场景:
- 前置校验(和现有用户名校验逻辑统一,用户体验最好)
在现有用户名重复校验的逻辑后,新增邮箱重复查询逻辑,在写入数据库前就拦截重复请求,改动最小:// 放在用户名存在校验的代码块之后 const existEmail = await User.findOne({ email }); if (existEmail) { throw new UserInputError('Email already registered', { errors: { email: '该邮箱已被注册' } }); } - 错误兜底捕获(解决并发场景下的校验穿透问题)
高并发场景下,两个相同邮箱的请求可能同时通过前置校验,最终触发数据库层的唯一键冲突,需要给save操作加异常捕获,识别E11000错误码返回自定义提示:let res; try { res = await newUser.save(); } catch (err) { if (err.code === 11000) { // 自动识别触发重复的字段,兼容用户名、邮箱等所有加了唯一索引的字段 const duplicateField = Object.keys(err.keyPattern)[0]; const errorMsg = duplicateField === 'email' ? '该邮箱已被注册' : `该${duplicateField}已被使用`; throw new UserInputError('Duplicate value error', { errors: { [duplicateField]: errorMsg } }); } // 非重复键错误直接抛出,不做额外处理 throw err; }
额外提醒
- 不要省略兜底捕获逻辑:前置校验存在时间差,无法100%拦截所有重复写入请求
- 密码写入数据库前必须做加盐哈希处理,禁止明文存储
- 你当前代码返回值里的
token未定义,需要在用户保存成功后生成对应的JWT令牌再返回,否则会触发引用错误
内容的提问来源于stack exchange,提问作者עמית שוקרון
相关产品推荐
相关产品推荐

