koa2+mongoose 报错CastError: Cast to ObjectId failed

项目大致业务是文章(article)下面有很多评论(comment),属于一对多的关系。

以下是新建comment,涉及到article表,其中的外键target_id指向article 的主键 _id 

有段业务逻辑是从article表中根据请求体中的target_id作为article的_id进行查找(findById),返回的article应该是一个article表的mongoose文档对象。

如果返回的文档对象为空时,抛出全局文件中配置好的错误notfound,终止创建评论,然后提示没有找到该文章。

static async createComment(ctx, next) {
    // 校验参数
    commentValidator(ctx);
    const { target_id } = ctx.request.body;
    console.log(target_id);
    const article = await ArticleModel.findById({_id:target_id});
    if (!article) {
      throw new global.errs.NotFound("没有找到相关文章");
    }
    await CommentModel.create(ctx.request.body);
    ctx.body = res.success("创建评论成功");
}

测试: 

然后当我使用postman进行测试接口时,我首先将存在的article _id作为comment target_id:

结果是成功,数据库当中也有

以下是终端的返回数据,对应我输入的article _id

然后我将不存在的article _id作为target_id进行添加comment,理论上来说应该会返回没有找到相关文章。然而返回结果并非如此:

postman 中返回的是配置好的另外一个未知错误,而不是我调用的not found.

以下是终端中报错:

提示的是 CastError: Cast to ObjectId failed 强制转换为对象id失败

  <-- POST /api/v1/comment
1234567890
  xxx POST /api/v1/comment 500 9ms -
false
CastError: Cast to ObjectId failed for value "{ _id: '1234567890' }" (type Object) at path "_id" for model "Article"
    at model.Query.exec (F:\gitlab\项目koablog\koa-server最新\node_modules\mongoose\lib\query.js:4498:21)
    at model.Query.Query.then (F:\gitlab\项目koablog\koa-server最新\node_modules\mongoose\lib\query.js:4592:15)
    at processTicksAndRejections (internal/process/task_queues.js:95:5) {
  messageFormat: undefined,
  stringValue: `"{ _id: '1234567890' }"`,
  kind: 'ObjectId',
  value: { _id: '1234567890' },
  path: '_id',
  reason: Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters

 尝试修改

1.我尝试改代码,把可能涉及到强制将字符串转换成对象的地方去掉。但是发现依然报同样的错

    const article = await ArticleModel.findById({target_id});

2.第二次尝试,考虑到mongoose 的findbyid应该会内部自动转化成monogo对象id

static async createComment(ctx, next) {
    // 校验参数
    commentValidator(ctx);
    const { target_id } = ctx.request.body;
    const _id = mongoose.Types.ObjectId(target_id);
    console.log(typeof _id);//控制台输出类型为object,说明转化类型有效

    const article = await ArticleModel.findById({_id});
    if (!article) {
      throw new global.errs.NotFound("没有找到相关文章");
    }
    await CommentModel.create(ctx.request.body);
    ctx.body = res.success("创建评论成功");
  }

结果报出以下错误: 

Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters

意思是 传入的mongoose参数必须是12个字节的单个字符串或24个十六进制字符的字符串 

  <-- POST /api/v1/comment
object
  --> POST /api/v1/comment 200 104ms 53b
  <-- POST /api/v1/comment
  xxx POST /api/v1/comment 500 2ms -
false
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters

3.第三次尝试,在model中发现target_id 创建时并未声明对文章article的ref引用,只是单纯的字符串。于是对CommentModel.js做出以下修改:

//models/CommentModel.js
// 1.引入mongoose
const mongoose = require("mongoose");

// 2.字义Schema(描述文档结构)
const CommentSchema = new mongoose.Schema({
  nickname: { type: String, require: true }, //评论人昵称
  content: { type: String, require: true }, //评论内容
  target_id: {//评论目标文章的id    
    type:mongoose.Schema.Types.ObjectId,
    //ref属性表示引用 可以直接引用Article模型
    ref:'Article' 
  }, 
});

// 3.定义Model(与几何对应,可以操作集合)
const CommentModel = mongoose.model("Comment", CommentSchema);

// 4.向外暴露model
module.exports = CommentModel;

 发现问题并没有解决,不过通过以上三次尝试,总结了经验。

通过几次输入请求体的尝试,发现了一个限制

输入的测试参数必须是12个字节的单个字符串或24个十六进制字符的字符串,也就是第二次尝试发现的问题。 

总结出来

  • 转换成mongoose的ObjectId 是没有错的,因为findById({})是要从数据库中通过id查找,数据库中的id数据也是ObjectId类型。
  • 对应数据库中这种类型,创建mongoose.Schema 时应该将引用type设置为等同于数据库中的类型,例如: target_id: {type:mongoose.Schema.Types.ObjectId,ref:'Article'}
  • 输入测试参数也就是koa中ctx.request.body时,应该将输入的id位数严格按照上面提到的限制。

以上总结接下来进行测试

1.CommentModel中的type已经修改好。

2.CommmentController,转换请求体参数类型

  static async createComment(ctx, next) {
    // 校验参数
    commentValidator(ctx);
    const { target_id } = ctx.request.body;
    //将引用id target_id 转换成mongoose ObjectId
    const _id = mongoose.Types.ObjectId(target_id);
    //位数限制,如果请求体测试id位数与mongoose生成的ObjectId不匹配,则会报未知错误
    //Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
    const article = await ArticleModel.findById({ _id });
    console.log(article);
    if (!article) {
      throw new global.errs.NotFound("没有找到相关文章");
    }
    await CommentModel.create(ctx.request.body);
    ctx.body = res.success("创建评论成功");
  }

3.postman测试参数:要达到的效果是未找到相关文章。

命令行终端: 

如果将输入的target_id少一位的话就会报未知错误,终端提示位数不对,如下删除一个3:

 

 命令行终端: 

 最后成功创建一个,让自己开心一把:

 终端命令行:

 成功返回源文章对象

 好了问题解决,congratulation

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值