180

当向不存在/customers/41224d776a326fb40f000001的文档发送请求时,是并且我正在返回:_id 41224d776a326fb40f000001docnull404

  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:路径“_id”处的值“foo”转换为 ObjectId 失败。

那么这个错误是什么?

4

29 回答 29

232

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.
}
于 2013-02-18T17:37:11.987 回答
65

使用现有函数检查 ObjectID。

var mongoose = require('mongoose');
mongoose.Types.ObjectId.isValid('your id here');
于 2014-09-11T22:35:34.397 回答
22

我不得不将我的路线移动到捕获路线参数的其他路线之上:

// 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);
于 2019-02-06T11:27:47.327 回答
17

我有同样的问题,我添加了
_id: String .in schema 然后它开始工作

于 2018-08-13T10:49:37.423 回答
13

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

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

ObjectId.fromString( myObjectIdString );
于 2013-02-18T16:48:02.093 回答
10

如果您有两条这样的不同路线,这可能是路线不匹配的情况

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

那么您必须小心地将使用参数的路线放在对我有用的常规路线之后

于 2021-03-11T11:35:35.207 回答
8

当您将无效的 id 传递给 mongoose 时,就会发生这种情况。所以在继续之前首先检查它,使用猫鼬isValid功能

import mongoose from "mongoose";

// add this inside your route
if( !mongoose.Types.ObjectId.isValid(id) ) return false;
于 2020-10-18T07:28:32.743 回答
7

就我而言,我必须添加_id: Object到我的架构中,然后一切正常。

于 2019-09-18T04:57:19.853 回答
5
 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"});
        }

最好是检查有效性

于 2018-01-22T15:52:03.497 回答
5

截至 2019 年 11 月 19 日

您可以isValidObjectId(id)从 mongoose 版本 5.7.12 开始使用

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

于 2020-05-13T16:32:01.213 回答
3

如果上述解决方案不适合您。检查您是否正在向POST路由发送GET请求。 对我来说就是这么简单和愚蠢。

于 2021-04-20T11:36:02.973 回答
2

我最近遇到了类似的问题,并通过捕获错误来确定它是否是 Mongoose 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);
    }
});
于 2020-05-02T14:49:34.203 回答
2

您还可以使用 ObjectId.isValid ,如下所示:

if (!ObjectId.isValid(userId)) return Error({ status: 422 })
于 2016-08-18T13:22:29.090 回答
2

您可以在查询中使用它之前验证每个 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;
  // ...
});

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

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;
    }
  };
}
于 2020-08-26T20:17:38.043 回答
1

我采用了 @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);
  }
};
于 2014-04-03T22:45:09.713 回答
1

我遇到了这个问题并修复了mongoose.ObjectId(id)没有Types

于 2020-08-07T01:51:26.063 回答
1
//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
}
于 2019-04-14T08:52:56.903 回答
1

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

于 2018-04-18T07:19:04.663 回答
1

ObjectId 由以下内容组成。

  1. 一个 4 字节的值,表示自 Unix 纪元以来的秒数
  2. 一个 5 字节的随机值(机器 ID 3 字节和处理器 ID 2 字节)
  3. 一个 3 字节的计数器,从一个随机值开始。

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

mongoose.Types.ObjectId.isValid(sample_object_id)
于 2018-10-29T03:04:23.777 回答
1

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

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);
    });

});

快速验证器版本 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);
        });

    });

});
于 2017-09-27T11:10:29.383 回答
0

检测和纠正 ObjectID 错误

我在尝试使用 mongoose 删除项目时偶然发现了这个问题并得到了同样的错误。查看返回字符串后,我发现返回的字符串中有一些额外的空格导致了我的错误。因此,我应用了此处提供的一些答案来检测错误的 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("/");
      }
    });
});

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

我希望这有帮助。

于 2019-06-21T18:08:37.187 回答
0

在我的情况下,参数id长度是 25,所以我修剪了参数id的第一个字符并尝试了。有效。

块引用

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

将字符串对象更改为 ObjectId 实例 fromString() 方法不再存在。有一个新方法 createFromHexString()。

const _id = mongoose.Types.ObjectId.fromString(id); // old method not available
const _id = mongoose.Types.ObjectId.createFromHexString(id); // new method.
于 2021-05-07T07:44:16.900 回答
0

或者你可以这样做

var ObjectId = require('mongoose').Types.ObjectId; var objId = new ObjectId( (param.length < 12) ? "123456789012" : param );

如此处所述,Mongoose 的带有 $or 条件的 find 方法无法正常工作

于 2017-02-12T07:14:11.550 回答
0

我有同样的错误,但情况与问题不同,但也许它对某人有用。

问题是添加带扣:

错误的:

    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 }

于 2021-01-28T06:55:23.770 回答
0

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

我喜欢反引号: `${id}`

这应该可以解决没有开销的问题

于 2018-09-28T14:04:08.313 回答
0

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

于 2022-01-10T11:45:15.467 回答
0

将字符串转换为 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
于 2018-05-11T13:03:37.237 回答
0

您所要做的就是将参数名称“id”更改为“_id”

于 2022-02-01T02:09:35.813 回答
-1

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

于 2020-02-01T09:08:04.280 回答