路径“ _ id”的值 XXX 失败时,抛向 ObjectId 的 Mongoose 错误是什么?

当向 /customers/41224d776a326fb40f000001发送一个请求而一个具有 _id 41224d776a326fb40f000001的文档不存在时,docnull,我返回一个 404:

  Controller.prototype.show = function(id, res) {
this.model.findById(id, function(err, doc) {
if (err) {
throw err;
}
if (!doc) {
res.send(404);
}
return res.send(doc);
});
};

然而,当 _id不符合 Mongoose 期望的“格式”(我认为)时,例如 GET /customers/foo就会返回一个奇怪的错误:

CastError: 对 ObjectId 的强制转换失败,路径“ _ id”的值为“ foo”。

那么这个错误是什么呢?

335886 次浏览

您是否将该字符串解析为 ObjectId

在我的应用程序中,我要做的是:

ObjectId.fromString( myObjectIdString );

Mongoose 的 findById方法将 id参数强制转换为模型的 _id字段的类型,这样它就可以正确地查询匹配的文档。这是一个 ObjectId,但是 "foo"不是有效的 ObjectId,因此强制转换失败。

41224d776a326fb40f000001不会发生这种情况,因为该字符串是有效的 ObjectId。

解决这个问题的一种方法是在 findById调用之前添加一个检查,以查看 id是否是有效的 ObjectId:

if (id.match(/^[0-9a-fA-F]{24}$/)) {
// Yes, it's a valid ObjectId, proceed with `findById` call.
}

我改编了@gustavohenke 解决方案,在 try-catch 包裹着原始代码中实现转换 ObjectId,以利用 ObjectId 转换的失败作为验证方法。

Controller.prototype.show = function(id, res) {
try {
var _id = mongoose.Types.ObjectId.fromString(id);






// the original code stays the same, with _id instead of id:


this.model.findById(_id, function(err, doc) {
if (err) {
throw err;
}
if (!doc) {
res.send(404);
}
return res.send(doc);
});






} catch (err) {
res.json(404, err);
}
};

使用现有函数检查 ObjectID。

var mongoose = require('mongoose');
mongoose.Types.ObjectId.isValid('your id here');

您还可以像下面这样使用 ObjectID. isValid:

if (!ObjectId.isValid(userId)) return Error({ status: 422 })

或者你可以这么做

类型: ObjectId; Var objecId = new ObjectId ((param.length < 12) ? “123456789012”: param) ;

正如这里提到的 Mongoose 的具有 $或条件的 find 方法不能正常工作

这是一个老问题,但是您也可以使用 Express-validator 包来检查请求参数

特快验证程序第4版(最新版本) :

validator = require('express-validator/check');


app.get('/show/:id', [


validator.param('id').isMongoId().trim()


], function(req, res) {


// validation result
var errors = validator.validationResult(req);


// check if there are errors
if ( !errors.isEmpty() ) {
return res.send('404');
}


// else
model.findById(req.params.id, function(err, doc) {
return res.send(doc);
});


});

Express-validator 版本3:

var expressValidator = require('express-validator');
app.use(expressValidator(middlewareOptions));


app.get('/show/:id', function(req, res, next) {


req.checkParams('id').isMongoId();


// validation result
req.getValidationResult().then(function(result) {


// check if there are errors
if ( !result.isEmpty() ) {
return res.send('404');
}


// else
model.findById(req.params.id, function(err, doc) {
return res.send(doc);
});


});


});
 if(mongoose.Types.ObjectId.isValid(userId.id)) {
User.findById(userId.id,function (err, doc) {
if(err) {
reject(err);
} else if(doc) {
resolve({success:true,data:doc});
} else {
reject({success:false,data:"no data exist for this id"})


}
});
} else {
reject({success:"false",data:"Please provide correct id"});
}

最好是检查有效性

始终使用 mongoose.Types.ObjectId('your id')for 条件在您的查询,它将验证的 id 字段之前,运行您的查询,结果您的应用程序不会崩溃。

将字符串强制转换为 ObjectId

import mongoose from "mongoose"; // ES6 or above
const mongoose = require('mongoose'); // ES5 or below


let userid = _id
console.log(mongoose.Types.ObjectId(userid)) //5c516fae4e6a1c1cfce18d77
我有同样的问题,我添加了 < br > _ id: String. in schema then it start work

解决这个问题的方法是将 id 转换为字符串

返回文章页面我喜欢这个标签译者: `${id}`

这应该可以在没有开销的情况下解决问题

更新2022年10月

如果您现在使用:

{id: id} // if you have an id property defined

或者

{_id: new ObjectId(id)} // and search for the default mongodb _id

ObjectId 由以下内容组成。

  1. 一个4字节的值,表示 Unix 时代以来的秒数
  2. 一个5字节的随机值(机器 ID 为3字节,处理器 ID 为2字节)
  3. < li > 3字节计数器,从一个随机 值

验证 objectId 是否有效的正确方法是使用 ObjectId 类本身的静态方法。

mongoose.Types.ObjectId.isValid(sample_object_id)

我不得不在其他路线上移动我的路线来捕捉路线参数:

// require express and express router


const express = require("express");
const router = express.Router();


// move this `/post/like` route on top


router.put("/post/like", requireSignin, like);


// keep the route with route parameter `/:postId` below regular routes


router.get("/post/:postId", singlePost);
//Use following to check if the id is a valid ObjectId?


var valid = mongoose.Types.ObjectId.isValid(req.params.id);
if(valid)
{
//process your code here
} else {
//the id is not a valid ObjectId
}

< 强 > 检测及更正 ObjectID 错误

当我试图用猫鼬删除一个条目时,我碰巧遇到了这个问题,并得到了相同的错误。在查看返回字符串之后,我发现在返回的字符串中有一些额外的空格,这给我造成了错误。因此,我应用了这里提供的一些答案来检测错误的 id,然后删除字符串中的额外空格。下面是我用来最终解决这个问题的代码。译注:

const mongoose = require("mongoose");
mongoose.set('useFindAndModify', false);  //was set due to DeprecationWarning: Mongoose: `findOneAndUpdate()` and `findOneAndDelete()` without the `useFindAndModify`






app.post("/delete", function(req, res){
let checkedItem = req.body.deleteItem;
if (!mongoose.Types.ObjectId.isValid(checkedItem)) {
checkedItem = checkedItem.replace(/\s/g, '');
}


Item.findByIdAndRemove(checkedItem, function(err) {
if (!err) {
console.log("Successfully Deleted " + checkedItem);
res.redirect("/");
}
});
});

这对我很有效,我假设如果其他项目开始出现在返回字符串中,它们也可以以类似的方式被删除。

希望这个能帮上忙。

在我的示例中,我必须将 _id: Object添加到 Schema 中,然后一切都工作正常。

我解决了这个问题,改变了路线的顺序。

我最近遇到过类似的情况,并通过捕获错误来查明它是否是猫鼬 ObjectId 错误来解决这个问题。

app.get("/:userId", (req, res, next) => {
try {
// query and other code here
} catch (err) {
if (err.kind === "ObjectId") {
return res.status(404).json({
errors: [
{
msg: "User not found",
status: "404",
},
],
});
}
next(err);
}
});

截至2019年11月19日

您可以使用猫鼬版本5.7.12中的 isValidObjectId(id)

https://mongoosejs.com/docs/api/mongoose.html#mongoose_Mongoose-isValidObjectId

我有这个问题和固定做 mongoose.ObjectId(id)没有 Types

您可以在查询中使用每个 ID 之前验证它(我认为这是最佳实践) ,

// Assuming you are using Express, this can return 404 automatically.
app.post('/resource/:id([0-9a-f]{24})', function(req, res){
const id = req.params.id;
// ...
});

... 或者你可以修补猫鼬来忽略这些施法错误,而是使用字符串表示来进行查询。您的查询当然不会找到任何东西,但这可能就是您希望发生的事情。

import { SchemaType }  from 'mongoose';


let patched = false;


export const queryObjectIdCastErrorHandler = {
install,
};


/**
* Monkey patches `mongoose.SchemaType.prototype.castForQueryWrapper` to catch
* ObjectId cast errors and return string instead so that the query can continue
* the execution. Since failed casts will now use a string instead of ObjectId
* your queries will not find what they are looking for and may actually find
* something else if you happen to have a document with this id using string
* representation. I think this is more or less how MySQL would behave if you
* queried a document by id and sent a string instead of a number for example.
*/
function install() {
if (patched) {
return;
}


patch();


patched = true;
}


function patch() {
// @ts-ignore using private api.
const original = SchemaType.prototype.castForQueryWrapper;


// @ts-ignore using private api.
SchemaType.prototype.castForQueryWrapper = function () {
try {
return original.apply(this, arguments);
} catch (e) {
if ((e.message as string).startsWith('Cast to ObjectId failed')) {
return arguments[0].val;
}


throw e;
}
};
}

当你给猫鼬传递一个无效的 id 时会发生这种情况。所以在继续之前首先检查它,使用猫鼬的 isValid函数

import mongoose from "mongoose";


// add this inside your route
if( !mongoose.Types.ObjectId.isValid(id) ) return false;

我有同样的错误,但在一个不同的情况下比在问题,但也许它会有用的人。

问题在于增加了扣环:

错:

    const gamesArray = [myId];


const player = await Player.findByIdAndUpdate(req.player._id, {
gamesId: [gamesArray]
}, { new: true }


正确:

    const gamesArray = [myId];


const player = await Player.findByIdAndUpdate(req.player._id, {
gamesId: gamesArray
}, { new: true }


如果您有两个不同的路由,这可能是路由不匹配的情况

router.route("/order/me") //should come before the route which has been passed with params
router.route("/order/:id")

那么你必须小心地将使用 param 的路线放在我曾经使用过的常规路线之后

如果以上方法对你不起作用。 检查是否向 职位路由发送 走开请求。 < br/> 对我来说就是这么简单和愚蠢

在我的例子中,参数 身份证的长度是25,所以我修剪了参数 身份证的第一个字符并尝试。成功了。

引用原文

const paramId = req.params.id;
if(paramId.length === 25){
const _id = paramId.substring(1, 25);
}

将字符串对象更改为 ObjectId instance from String ()方法已不再存在。

const _id = mongoose.Types.ObjectId.fromString(id); // old method not available
const _id = mongoose.Types.ObjectId.createFromHexString(id); // new method.

我在以下方面也犯了同样的错误:

Model.find({})

我在查询中使用了 GET 请求,当我把它改为 POST 时,它还是起作用了。

如果将少于或多于24个字符的字符串作为 id 发送,则可能发生

您只需将参数名“ id”更改为“ _ id”即可

在我的案例中,类似的路线导致了这个问题。

Router.get("/:id", getUserById);
Router.get("/myBookings",getMyBookings);

在上面的代码中,无论何时发出路由“/myBooking”的 get 请求,它都会转到第一个路由,其中 req.params.id 等于“ myBooking”,而这不是一个有效的 ObjectId。

它可以通过使两条路径不同来纠正。

就像这样

Router.get("/user/:id", getUserById);
Router.get("/myBookings",getMyBookings);
您正在出现 castError,因为在 id 路由之后调用的下一个路由无法附加到 id 路由。 您必须将 id 路由声明为最后一个路由

如果正在使用 findByIdAndDelete 方法,则验证 {_id:id}此对象。

this.model.findByIdAndDelete({_id:id}).exec()