解决Mongoose出现MongoServerError: E11000 duplicate key error collection的报错

背景

最近在做基于express+mongoose的个人博客项目开发,但在新增用户的时候却出现了这个错误:

MongoServerError: E11000 duplicate key error collection: test.users index: username_1 dup key: { username: "123abc" }

在这里插入图片描述

错误分析

duplicate key error collection 是指在数据库集合中出现了重复键错误。在数据库中,集合是一组相关的文档存储的容器,而键则是用于唯一标识每个文档的字段。
当尝试向数据库集合插入一个新文档时,如果该文档中包含了一个与已有文档中某个键的值相同的键,就会触发重复键错误。

那么就很容易知道,我的当前报错信息是:在 test.users 集合中,username 字段的值为 “123abc” 的文档已经存在,并且该字段被设置为了唯一索引。事实也是如此。

因为我的项目需求是:当用户注册的时候,用户名是不允许重复的,是唯一的,所以我在Schema中为username设置unique: true属性。
在这里插入图片描述
当我初次注册用户的时候是可以正常打印出用户的信息的,
在这里插入图片描述
当我再次运行代码(也就是再以相同的用户信息注册一次),这时候就会出现重复键的问题。也就是出现了上面所说的报错。

解决方案

现在的业务场景是用户注册,并且用户名是不允许重复的(在我的项目是这样规定的)。下面有几种解决方法:

  • 修改字段的值:即文档以存在 username 值为 “123abc”,那么就需要修改username的值,不能为"123abc";
  • 更新以存在的文档:使用 updateOne()updateMany() 方法,通过查询条件更新文档;
  • 删除已存在的文档:可以使用 deleteOne()deleteMany() 方法删除已存在的文档,然后再插入新的文档。

上面的方法虽然可以解决问题,但是用户注册的时候难免会出现用户名重复的情况,一旦这个情况出现就会出现报错,程序就会终止,用户就无法再次进行注册。我们要做的就是处理这个报错,并且告诉用户是用户名重复的原因导致无法注册,提示用户更换一个username。

最后的解决方案:

因为是用mongoose开发的嘛,所以我们可以去mongoose文档看看有没有有关这种错误的处理。错误信息的关键信息在于duplicate key error,而当时我是在用Validation给SchemaType做校验,而刚好文档里又说到unique不是一个校验器,那么它必然会给出处理使用unique出现的错误处理。
在这里插入图片描述
文档给出了当出现了duplicate key error该如何处理,而也是受到这个启发,我就对duplicate key error做了处理。

U2.init().
  then(() => U2.create(dup)).
  catch(error => {
    // `U2.create()` will error, but will *not* be a mongoose validation error, it will be
    // a duplicate key error.
    // See: https://masteringjs.io/tutorials/mongoose/e11000-duplicate-key
    assert.ok(error);
    assert.ok(!error.errors);
    assert.ok(error.message.indexOf('duplicate key error') !== -1);
  });

try catch捕捉异常错误,捕捉到的错误有两种情况:一种是重复键的问题,另一种是mongoose内置的校验器设置的错误。两种错误有不同的处理方法。如果查询错误信息有duplicate key error,就return false
在这里插入图片描述
如果没有的话,就是mongoose内置的校验器设置的错误,对它做进一步的处理。

在这里插入图片描述
mongoose内置的校验器设置的错误可以自定义的,比如像我这里这样:

username: {
    type: String,
    required: [true, 'username required'],  // 必填项
    unique: true, // 唯一项
    validate: {
      validator(value) {
        return /^(?!\d+$)(?![a-zA-Z]+$)[a-zA-Z0-9]{6,8}$/.test(value)
      },
      message: props => `${props.value} is not a valid username!`
    }
  }

完整的demo代码:

let schema = new mongoose.Schema({
  username: {
    type: String,
    required: [true, 'username required'],  // 必填项
    unique: true, // 唯一项
    validate: {
      validator(value) {
        return /^(?!\d+$)(?![a-zA-Z]+$)[a-zA-Z0-9]{6,8}$/.test(value)
      },
      message: props => `${props.value} is not a valid username!`
    }
  },
  // 密码不需要加validate验证条件,因为前端发送过来的密码是经过加密的
  passowrd: {
    type: String,
    select: false,
    required: [true, 'passowrd required'],
    set(value) {  // 加密密码
      return encrypt(value)
    }
  },
  email: {
    type: String,
    unique: true,
    validate: {
      validator(value) {
        return /^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/.test(value)
      },
      message: props => `${props.value} is not a valid email!`
    }
  },
  avatar: {
    type: String,
    default: "http://127.0.0.1:3000/public/images/avatar.jpg"
  },
  nikname: {
    type: String,
    validate: {
      validator(value) {
        return /^([\w\W]){1,8}$/.test(value)
      },
      message: props => `${props.value} is not a valid nikname!`
    },
    default: "用户"
  }
})

let User = mongoose.model('User', schema)
User.create({
  username: "123abc",
  passowrd: "1234cdf",
  email: "example@example.com"
}).then(data => {
  console.log(data);
}).catch(err => {
  // console.log(err.message);  // E11000 duplicate key error collection: test.users index: username_1 dup key: { username: "123abc" }
  if (err.message.indexOf('duplicate key error') !== -1) {
    console.log('存在重复键', err.keyPattern);
    return false
  }
  Object.entries(err).map(([key, value]) => {
    console.log(`error: ${key}, ${value.message} `)
  })
})

这里是模拟用户注册的功能,里面的错误处理得不是很细致,但在真正运用到项目中的话是会把错误信息处理得更细致一些的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_北冥有鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值