我有一些Mongoose代码在NodeJS上运行失败.

相关的行是:

// get data from database
const existingUser = await User.findOne({ userId });

userId是一种"数字"类型.

console.log(typeof userId); // -> number
console.log(userId);        // -> 1

该值从http请求的正文中获得,其中正文被编码为JSON.

因此,我并不惊讶地发现类型是"number",因为所有的JSON数字都是相同的(浮点数,没有整数),但我惊讶地发现这会导致错误,Mongoose无法将数字转换为整数类型.

在这里,请求由EXPRESS中定义的POST端点接收.


app.post("/test", async (request, response) => {
    // get user id from body of request

    console.log(request.body); // -> { userId: 1 }

    const userId = request.body.userId;

    // ...

我怎么才能解决这个问题呢?

准确的错误是:

CastError: Cast to ObjectId failed for value "1" (type number) at path "userId" for model "User"

reason: BSONError: input must be a 24 character hex string, 12 byte Uint8Array, or an integer

相关的模式是:

const user = new mongoose.Schema({
  userId: {
    type: mongoose.Types.ObjectId,
    unique: true,
    required: true,
  },
});

推荐答案

当您在类型为ObjectId的属性上查询集合时,Mongoose将在内部将您的查询参数值转换为ObjectId,以便执行相等性判断.这个很方便.

然而,一个很大的困惑来源是,像ObjectId("6578c06c3f3559a5e969fc88")这样的ObjectId是由以下组成的:

一个4字节的时间戳,表示对象ID的创建,自Unix时代以来以秒为单位.

[6578c06c]3f3559a5e969fc88

每个进程生成一次的5字节随机值.该随机值对于机器和过程是唯一的.

6578c06c[3f3559a5e9]69fc88

3字节递增计数器,初始化为随机值.

6578c06c3f3559a5e9[69fc88]

在您的例子中,您有一个数字userId

request.body.userId === 1 // true

Mongoose认为User.userIdObjectId,并试图将您的userId值转换为ObjectId,但失败了,因此它抛出Cast to ObjectId failed错误.

作为解决办法,您所做的是使用mongoose.Types.ObjectId()函数生成一个新的ObjectId,然后使用该值查找匹配项.然而,该函数不仅接受有效的24个字符的十六进制字符串,还接受一个整数.

您认为您已经将userId值转换为与文档匹配的ObjectId,但您所做的只是创建了一个ObjectId,该ObjectIdUnix Epoch时间戳之后1秒,并结合了5字节的随机值和3字节的递增计数器.要说明以下情况:

console.log(new mongoose.Types.ObjectId(1));
// ObjectId('0000000108423469423cf5f1')

console.log(new mongoose.Types.ObjectId(1));
// ObjectId('0000000108423469423cf5f5')

console.log(new mongoose.Types.ObjectId(1));
// ObjectId('0000000108423469423cf5f9')

您的值1已创建新ObjectId00000001部分.你可以运行它一百万次,你总是会得到同样的00000001.

我非常肯定您不会有任何在世界标准时间1970年1月1日00:00:01创建的文档,所以虽然您的查询不会出错,但它不会找到任何文档.

您所需要做的就是允许您的前端发送一个有效的字符串格式,例如{ userId : "6578c06c3f3559a5e969fc88" },然后Mongoose将完成其余的工作.

Node.js相关问答推荐

使用prisma迁移到SQL失败

MongoDB:更新批量操作中许多不能正常工作的内容

如何使用MongoDB在Node.js 中向数组中添加项?

如何使用聚合管道交换键值对

如何在Reaction应用程序中查看存储的斑点图像?

Node js 处理回调和 Promise

PM2 是否需要成为其托管项目的依赖项?

Axios TypeError:将循环 struct 转换为 JSON

无服务器部署使用无服务器组合抛出`spawn serverless ENOENT`

将环境变量从 GitHub 操作传递到 json

图像存储在后端文件夹中,但使用 multer 和 react.js 在前端找不到

使用mongoose 创建新文档并仅取回选定的字段

ESLint:TypeError:this.libOptions.parse 不是函数

用户与mongoose 的完美搭配

Mongoose-更新嵌套数组

适用于 Windows 的 NVM 无法正常工作?

使用restify时如何支持cors

npm 出现无法读取依赖项错误

Heroku + Node:找不到模块错误

使用 Monit 而不是基本的 Upstart 设置有什么好处?