【问题标题】:What's Mongoose error Cast to ObjectId failed for value XXX at path "_id"?什么是 Mongoose 错误 Cast to ObjectId failed for value XXX at path "_id"?
【发布时间】:2013-02-03 03:22:54
【问题描述】:

当向/customers/41224d776a326fb40f000001 发送请求并且带有_id 41224d776a326fb40f000001 的文档不存在时,doc 是null,我返回一个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: 路径“_id”处的值“foo”转换为 ObjectId 失败。

那么这个错误是什么?

【问题讨论】:

    标签: mongodb mongoose


    【解决方案1】:

    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.
    }
    

    【讨论】:

    • @Gremo 您只能在 Mongoose 架构中选择一种用于 _id 的类型。在"bla" 的情况下,您将使用String 类型而不是默认的ObjectId,并且您不需要添加此检查,因为任何内容都可以转换为字符串。
    • 我明白,但我想避免这个检查。如何从给定的字符串(来自GET 请求)创建一个新的ObjectId 以将其传递给findById 方法?
    • @Gremo 你不能。您只能从 24 个十六进制字符串构造 ObjectId。
    • 您可以使用 find({_id: yourId},...) 来查询具有该(唯一)ID 的文档。那,JohnnyHK 将 _id 添加到您的架构(使用您想要的“字符串”类型)的答案是您问题的完整解决方案。
    • 如今,12 个字符串也可以转换为 ObjectId。 ObjectId("000000000000") --> 303030303030303030303030
    【解决方案2】:

    使用现有函数检查 ObjectID。

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

    【讨论】:

    • 小心使用该方法,因为它具有将任何 12 字节字符串视为有效的奇怪行为。因此,它甚至为您的 'your id here' 示例返回 true。 github.com/mongodb/js-bson/issues/106
    • console.log("这里");让 i = new mongoose.Types.ObjectId(userId.id); console.log("现在在这里"); // 这个控制台甚至不打印
    【解决方案3】:

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

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

    【讨论】:

    • 这对我有用。我很好奇这个错误背后的原因。您能否解释一下将路线移动到常规路线下方是如何导致错误消失的?
    • 这对我也很有效。看起来 /test/create 用 id=create 满足了这个 /test/:id。并且字符串不能转换为_id。
    • @kaila88,你的评论很合乎逻辑,发生的方式也一样。
    • 这对我也有用,+1!一旦你看到它就非常简单,但它让我发疯了,因为异常让我认为我的数据库文档 ID 有问题。 :D
    【解决方案4】:

    我遇到了同样的问题
    _id: String .in schema 然后开始工作

    【讨论】:

    • 一年后,这在使用 connect-mongo 时救了我
    • 感谢您在连续工作 15 小时后卡在一个小点上。
    【解决方案5】:

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

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

    ObjectId.fromString( myObjectIdString );
    

    【讨论】:

    • 是的,你应该这样做,因为你查询的是 ObjectId 类型,所以需要强制转换。
    • 试试mongoose.Types.ObjectId。
    • 有效,但在传递“foo”时我得到“无效的 ObjectId”。那么,如果它可能会失败,那么从字符串创建 ObjectId 有什么意义呢?
    • 根据 MongoDB 文档,ObjectIds 只能是 24 个十六进制字节。
    • fromString 不是函数
    【解决方案6】:

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

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

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

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

        【解决方案8】:

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

        【讨论】:

          【解决方案9】:
           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"});
                  }
          

          最好是检查有效性

          【讨论】:

            【解决方案10】:

            截至 2019 年 11 月 19 日

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

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

            【讨论】:

              【解决方案11】:

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

              【讨论】:

                【解决方案12】:

                你也可以像下面这样使用 ObjectId.isValid :

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

                【讨论】:

                • ReferenceError: ObjectId 未定义
                【解决方案13】:

                我最近遇到了类似的问题,并通过捕获错误来确定它是否是 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);
                    }
                });
                

                【讨论】:

                  【解决方案14】:

                  您可以在查询中使用每个 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;
                      }
                    };
                  }
                  

                  【讨论】:

                    【解决方案15】:

                    我采用了 @gustavohenke 解决方案的改编版,在 围绕原始代码的 try-catch 中实现了 cast 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);
                      }
                    };
                    

                    【讨论】:

                    【解决方案16】:

                    这是一个老问题,但您也可以使用 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);
                        });
                    
                    });
                    

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

                    【讨论】:

                      【解决方案17】:

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

                      【讨论】:

                        【解决方案18】:
                        //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
                        }
                        

                        【讨论】:

                        • 还有其他答案提供了 OP 的问题,它们是多年前发布的。发布答案时,请确保添加新的解决方案或更好的解释,尤其是在回答较旧的问题时。仅代码的答案被认为是低质量的:请务必解释您的代码的作用以及它如何解决问题。
                        【解决方案19】:

                        我遇到了这个问题并修复了在没有Types 的情况下执行mongoose.ObjectId(id)

                        【讨论】:

                          【解决方案20】:

                          ObjectId 由以下内容组成。

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

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

                          mongoose.Types.ObjectId.isValid(sample_object_id)
                          

                          【讨论】:

                            【解决方案21】:

                            或者你可以这样做

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

                            这里提到Mongoose's find method with $or condition does not work properly

                            【讨论】:

                              【解决方案22】:

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

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

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

                              【讨论】:

                                【解决方案23】:

                                将字符串转换为 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
                                

                                【讨论】:

                                  【解决方案24】:

                                  检测和纠正 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("/");
                                        }
                                      });
                                  });
                                  

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

                                  我希望这会有所帮助。

                                  【讨论】:

                                    【解决方案25】:

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

                                    问题在于添加带扣:

                                    错误:

                                        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 }
                                    
                                    

                                    【讨论】:

                                      【解决方案26】:

                                      在我的例子中,参数 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.
                                      

                                      【讨论】:

                                        【解决方案27】:

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

                                        【讨论】:

                                          【解决方案28】:

                                          您只需将参数名称“id”更改为“_id”

                                          【讨论】:

                                            【解决方案29】:

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

                                            【讨论】:

                                            • 这似乎不是一个答案。充其量只是一条评论。
                                            • 这对我有用,我有 2 条博客路由:'/blogs/create' 和 'blogs/:id'。后者按路线顺序排在第一位。所以当我去'/blogs/create'时,猫鼬把'create'作为id
                                            猜你喜欢
                                            • 2017-02-06
                                            • 2017-08-14
                                            • 2017-03-23
                                            • 2021-04-20
                                            • 2022-06-14
                                            • 2017-04-04
                                            • 2020-12-10
                                            • 2023-04-02
                                            相关资源
                                            最近更新 更多