Quelle est l'erreur Mongoose Cast to ObjectId a échoué pour la valeur XXX au chemin «_id»?


122

Lors de l'envoi d'une demande à /customers/41224d776a326fb40f000001et un document avec _id 41224d776a326fb40f000001n'existe pas, docest nullet je retourne un 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);
    });
  };

Cependant, quand _idne correspond pas à ce que Mongoose attend comme "format" (je suppose) par exemple avec GET /customers/fooune erreur étrange est retourné:

CastError: échec de la conversion en ObjectId pour la valeur "foo" au chemin "_id".

Alors, quelle est cette erreur?

Réponses:


182

La findByIdméthode de Mongoose convertit le idparamètre en type de _idchamp du modèle afin qu'il puisse correctement rechercher le document correspondant. Il s'agit d'un ObjectId mais "foo"pas d'un ObjectId valide, donc le cast échoue.

Cela ne se produit pas 41224d776a326fb40f000001car cette chaîne est un ObjectId valide.

Une façon de résoudre ce problème consiste à ajouter une vérification avant votre findByIdappel pour voir si idun ObjectId est valide ou non:

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

4
@Gremo Vous ne pouvez choisir qu'un seul type à utiliser _iddans votre schéma Mongoose. Dans le "bla"cas où vous utiliseriez un type de Stringau lieu de la valeur par défaut ObjectIdet vous n'auriez pas besoin d'ajouter cette vérification car tout peut être converti en chaîne.
JohnnyHK

2
Je comprends, mais j'aimerais éviter cette vérification. Comment puis-je créer un nouveau à ObjectIdpartir d'une chaîne donnée (à partir de la GETrequête) pour le transmettre à la findByIdméthode?
gremo

@Gremo Vous ne pouvez pas. Vous ne pouvez construire des ObjectIds qu'à partir de 24 chaînes de caractères hexadécimaux.
JohnnyHK

1
Vous pouvez simplement utiliser find ({_ id: yourId}, ...) pour rechercher le document avec cet identifiant (unique). Cela, et la réponse de JohnnyHK pour ajouter _id à votre schéma (avec le type de chaîne souhaité) est la solution complète à votre problème.
Steve Hollasch

1
De nos jours, 12 chaînes de caractères peuvent également être converties en ObjectId. ObjectId("000000000000") --> 303030303030303030303030
Dan Ross

50

Utilisez les fonctions existantes pour vérifier ObjectID.

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

15
Attention à utiliser cette méthode car elle a le comportement curieux de traiter toute chaîne de 12 octets comme valide. Donc, cela retourne même vrai pour votre 'your id here'exemple. github.com/mongodb/js-bson/issues/106
JohnnyHK

console.log ("ici"); let i = new mongoose.Types.ObjectId (userId.id); console.log ("maintenant ici"); // cette console ne
s'imprime

11

Analysez-vous cette chaîne comme ObjectId?

Ici, dans mon application, ce que je fais est:

ObjectId.fromString( myObjectIdString );

Oui, vous devriez, car vous interrogez un type ObjectId, le cast est donc nécessaire.
gustavohenke

1
Essayez mongoose.Types.ObjectId.
gustavohenke

1
Fonctionne, mais j'obtiens "ObjectId invalide" en passant "foo". Alors, quel est l'intérêt de créer un ObjectId à partir d'une chaîne, s'il peut échouer?
gremo

Selon la documentation MongoDB, les ObjectIds doivent être de 24 octets hexadécimaux uniquement.
gustavohenke

1
fromStringn'est pas une fonction
WasiF

8

J'ai le même problème que j'ajoute
_id: String .in schema puis il commence à fonctionner


un an plus tard, cela m'a sauvé lors de l'utilisation avec connect-mongo
Ren44

Merci d'être coincé à un petit moment après avoir travaillé pendant 15 heures d'affilée.
Black Mamba

8

J'ai dû déplacer mes itinéraires par-dessus d'autres itinéraires qui captent les paramètres de l'itinéraire:

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

C'était ça. J'aurais aimé trouver votre réponse il y a une heure. À votre santé!
Sodbileg Gansukh

Cela a fonctionné pour moi. Je suis curieux de connaître la raison de cette erreur. Pourriez-vous s'il vous plaît expliquer comment le déplacement de l'itinéraire sous les itinéraires réguliers a fait disparaître l'erreur?
Vishwak

Cela m'a également fonctionné. On dirait que / test / create satisfait ceci / test /: id avec id = create. et la chaîne ne peut pas être convertie en id.
kaila88

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

le mieux est de vérifier la validité


3

Dans mon cas, j'ai dû ajouter _id: Objectdans mon schéma, puis tout a bien fonctionné.


2

Vous pouvez également utiliser ObjectId.isValid comme suit:

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

1
ReferenceError: ObjectId n'est pas défini
torbenrudgaard

2
//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
}

Il y a d'autres réponses qui répondent à la question du PO, et elles ont été publiées il y a de nombreuses années. Lorsque vous publiez une réponse, assurez-vous d'ajouter une nouvelle solution ou une explication nettement meilleure, en particulier lorsque vous répondez à des questions plus anciennes. Les réponses au code uniquement sont considérées comme de mauvaise qualité: assurez-vous de fournir une explication de ce que fait votre code et de la manière dont il résout le problème.
help-info.de

2

J'ai été confronté à quelque chose de similaire récemment et je l'ai résolu en détectant l'erreur pour savoir s'il s'agissait d'une erreur 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);
    }
});


1

J'ai opté pour une adaptation de la solution @gustavohenke, implémentant le cast ObjectId dans un try-catch enroulé autour du code d'origine pour tirer parti de l'échec du casting ObjectId comme méthode de validation.

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

1
Cela aurait été agréable à utiliser, mais fromString () n'existe plus: github.com/Automattic/mongoose/issues/1890
Brent Washburne

1

C'est une vieille question mais vous pouvez également utiliser le package express-validator pour vérifier les paramètres de la demande

express-validator version 4 (dernière):

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

    });

});

1

Utilisez toujours mongoose.Types.ObjectId('your id')des conditions dans votre requête, il validera le champ id avant d'exécuter votre requête, par conséquent, votre application ne plantera pas.



0

La façon dont je résout ce problème transforme l'identifiant en une chaîne

je l'aime bien avec le backtick: `${id}`

cela devrait résoudre le problème sans frais généraux


0

ObjectId est composé des éléments suivants.

  1. une valeur de 4 octets représentant les secondes depuis l'époque Unix
  2. une valeur aléatoire de 5 octets (ID machine 3 octets et ID processeur 2 octets)
  3. un compteur de 3 octets, commençant par une valeur aléatoire.

La méthode correcte pour valider si objectId est valide consiste à utiliser la méthode statique de la classe ObjectId elle-même.

mongoose.Types.ObjectId.isValid (sample_object_id)


0

Convertir la chaîne en 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

0

Détection et correction de l'erreur ObjectID

Je suis tombé sur ce problème en essayant de supprimer un élément à l'aide de la mangouste et j'ai eu la même erreur. Après avoir examiné la chaîne de retour, j'ai trouvé qu'il y avait des espaces supplémentaires à l'intérieur de la chaîne retournée qui ont causé l'erreur pour moi. J'ai donc appliqué quelques-unes des réponses fournies ici pour détecter l'ID erroné, puis supprimer les espaces supplémentaires de la chaîne. Voici le code qui a fonctionné pour moi pour enfin résoudre le problème.

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("/");
      }
    });
});

Cela a fonctionné pour moi et je suppose que si d'autres éléments commencent à apparaître dans la chaîne de retour, ils peuvent être supprimés de la même manière.

J'espère que ça aide.


0

J'ai corrigé ce problème en modifiant l'ordre des itinéraires.


Cela ne semble pas être une réponse. Au mieux, c'est un commentaire.
MS le

Cela a fonctionné pour moi, j'avais 2 routes pour les blogs: '/ blogs / create' et 'blogs /: id'. Et ce dernier est venu en premier dans l'ordre des itinéraires. Donc, quand je suis allé à la mangouste '/ blogs / create', j'ai pris le 'create' comme id
Wyrone le

0

J'avais des problèmes avec cela et j'ai résolu de m'en mongoose.ObjectId(id)passerTypes

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.