Mongoose: Beste Möglichkeit, eine ObjectId zu validieren

Erstellt am 12. März 2014  ·  29Kommentare  ·  Quelle: Automattic/mongoose

Ich füge einige Objekt-IDs als Referenzen ein und möchte sicherstellen, dass es sich um gültige Objekt-IDs handelt (im Moment bekomme ich einen Absturz, wenn es sich nicht um gültige IDs handelt).

Ich überprüfe, ob sie auf gültige Objekte verweisen, indem ich einfach einen find()-Aufruf mache.

Wie soll ich überprüfen, ob die Zeichenfolgen das richtige Format haben? Ich habe einige Regexs gesehen, die prüfen, ob die Zeichenfolge eine Zeichenfolge mit 24 Zeichen ist, und solche Sachen - scheint ein Hack zu sein. Gibt es einen internen Validator für die ObjectId? Ich konnte nicht herausfinden, wie man es benutzt. Vielen Dank!

Hilfreichster Kommentar

Das funktioniert bei mir:

var mongoose = require('./node_modules/mongoose');
console.log(mongoose.Types.ObjectId.isValid);
// [Funktion: isValid]
console.log(mongoose.Types.ObjectId.isValid('53cb6b9b4f4ddef1ad47f943'));
// wahr
console.log(mongoose.Types.ObjectId.isValid('bleurgh'));
// falsch

Alle 29 Kommentare

Ich habe das gleiche Problem. Ich habe mongoose.Schema.ObjectId.isValid() sowie mongoose.Types.ObjectId.isValid() ausprobiert, aber keine dieser Eigenschaften hat eine isValid-Methode. Wie haben Sie dieses Problem letztendlich gelöst? Ich sehe auch, dass Mongodb einen hat und es gibt auch Regex als weitere Option. Ich würde es vorziehen, Regex nicht zu verwenden oder zu benötigen ('mongodb')

Das funktioniert bei mir:

var mongoose = require('./node_modules/mongoose');
console.log(mongoose.Types.ObjectId.isValid);
// [Funktion: isValid]
console.log(mongoose.Types.ObjectId.isValid('53cb6b9b4f4ddef1ad47f943'));
// wahr
console.log(mongoose.Types.ObjectId.isValid('bleurgh'));
// falsch

Übrigens ist die Methode in mongodb die bson lib-Methode, die nur auf nicht null, 12 oder 24 Zeichen Hex-String prüft - es ist eine Regex wie diese:

var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");

Es kann also nicht zu hacky sein, wenn es dort verwendet wird

sValid() gibt immer True zurück, wenn der String 12 Buchstaben enthält

console.log(mongoose.Types.ObjectId.isValid("zzzzzzzzzzzz")); // wahr

Da "zzzzzzzzzzzz" technisch gesehen eine gültige ObjectId ist, ist das einzige definierende Merkmal einer Objekt-ID, dass sie 12 Bytes lang ist. Siehe mongodb/js-bson#112. Ein JS-String der Länge 12 hat 12 Bytes, Modulo-Unicode-Verrücktheiten. Wenn Sie nach einer Zeichenfolge der Länge 24 Hex suchen möchten, prüfen Sie einfach, ob die Zeichenfolge mit /^[a-fA-F0-9]{24}$/ übereinstimmt

"zzzzzzzzzzzz" ist keine gültige Objekt-ID. Zum Beispiel Auflistung der Mongo-Shell (Mongodb-Version - 3.0.2):

> ObjectId('zzzzzzzzzzzz')
2015-04-29T18:05:20.705+0300 E QUERY    Error: invalid object id: length
    at (shell):1:1
> ObjectId('zzzzzzzzzzzzzzzzzzzzzzzz')
2015-04-29T18:06:09.773+0300 E QUERY    Error: invalid object id: not hex
    at (shell):1:1
> ObjectId('ffffffffffff')
2015-04-29T18:09:17.303+0300 E QUERY    Error: invalid object id: length
    at (shell):1:1
> ObjectId('ffffffffffffffffffffffff')
ObjectId("ffffffffffffffffffffffff")

Weil der ObjectId-Konstruktor der Mongodb-Shell so geschrieben ist, dass er nur Hex-Strings akzeptiert. Es ist der Einfachheit halber eine Einschränkung in der Mongo-Shell, nicht mit dem BSON-Typ ObjectId. Zugegebenermaßen ist dies ein etwas kontraintuitiver Fall, da ObjectIds normalerweise in Hex-Strings dargestellt werden, aber wenn Sie es nicht mögen, verwenden Sie einfach die Regex /^[a-fA-F0-9]{24}$/ :)

Warum werden wir falsch, wenn wir versuchen, isValid für eine ObjectId selbst und nicht für einen String auszuführen? Sollte dies nicht wahr zurückgeben, da ObjectId eine gültige ObjectId ist? Das ergibt keinen Sinn – vielleicht .toString() aufrufen, wenn es ein Objekt ist, das an isValid übergeben wird?

Kommentare von @niftylettuce sind willkommen unter #3365. Im Moment verweisen wir nur auf die Funktion ObjectId.isValid() des bson-Pakets , die nicht genau damit übereinstimmt, wie die Leute über ObjectIds in Mungo denken. Ich werde einen PR für die Rückgabe von true öffnen, wenn Sie jedoch eine ObjectId erhalten, das scheint vollkommen vernünftig.

Um auf ein etwas altes Problem zurückzukommen ... @atcwells Lösung mongoose.Types.ObjectId.isValid('53cb6b9b4f4ddef1ad47f943') funktioniert gut genug für mich, aber es scheint immer noch ein bisschen hackig zu sein, meinen Controller prüfen zu lassen, ob eine Objekt-ID gültig ist – wann Sicherlich ist es ein ziemlich häufiger Anwendungsfall, eine falsch formatierte ID an den Server senden zu wollen, ohne dass dieser abstürzt.

Im Idealfall würde es einfach etwas in err im Callback zurückgeben, damit wir es richtig handhaben und den richtigen HTTP-Status über unseren Controller senden können.

Gibt es einen Anwendungsfall, in dem dies keine nützliche Funktionalität im Kern wäre? Wenn nicht, können wir vielleicht ein Plugin erstellen. Ich habe eine schnelle Suche durchgeführt und es scheint nichts zu geben, was den Job macht – https://github.com/CampbellSoftwareSolutions/mongoose-id-validator dient der Validierung, dass die ID tatsächlich existiert, was nicht der Fall ist was wir hier tun wollen – wir wollen einfach sicherstellen, dass wir keinen nicht abgefangenen Fehler generieren.

Im Moment muss ich in meinem Express-Controller jedes Mal, wenn ich eine Anfrage gehe, die eine ObjectId enthält, z. B. GET an https://myproject/organisations/ {id}, so etwas tun:

if( !mongoose.Types.ObjectId.isValid(id) ){
    return res.sendStatus(400); // They didn't send an object ID
  }

... bevor ich dann Organisation.findOne(); mache

Scheint ziemlich Boilerplate. Ich schreibe gerne ein Plugin oder ähnliches, wenn mich jemand in die richtige Richtung weisen kann, wo ich anfangen soll. Scheint nicht wie ein Plugin zu sein, da es nicht wirklich eine Schema-Sache ist ...

@shankiesan Sie müssen das nicht tun, Mongoose lehnt das Abfrageversprechen ab, wenn die ID ungültig ist.

var assert = require('assert');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/test');
mongoose.set('debug', true);

var MyModel = mongoose.model('test', new Schema({ name: String }));

MyModel.findOne({ _id: 'invalid' }).exec().catch(error => console.error('error', error));

Ausgabe:

$ node gh-1959.js 
error { CastError: Cast to ObjectId failed for value "invalid" at path "_id"
    at MongooseError.CastError (/home/val/Workspace/10gen/troubleshoot-mongoose/node_modules/mongoose/lib/error/cast.js:19:11)
    at ObjectId.cast (/home/val/Workspace/10gen/troubleshoot-mongoose/node_modules/mongoose/lib/schema/objectid.js:147:13)
    at ObjectId.castForQuery (/home/val/Workspace/10gen/troubleshoot-mongoose/node_modules/mongoose/lib/schema/objectid.js:187:15)
    at cast (/home/val/Workspace/10gen/troubleshoot-mongoose/node_modules/mongoose/lib/cast.js:174:32)
    at Query.cast (/home/val/Workspace/10gen/troubleshoot-mongoose/node_modules/mongoose/lib/query.js:2563:10)
    at Query.findOne (/home/val/Workspace/10gen/troubleshoot-mongoose/node_modules/mongoose/lib/query.js:1239:10)
    at /home/val/Workspace/10gen/troubleshoot-mongoose/node_modules/mongoose/lib/query.js:2163:21
    at new Promise.ES6 (/home/val/Workspace/10gen/troubleshoot-mongoose/node_modules/mongoose/lib/promise.js:45:3)
    at Query.exec (/home/val/Workspace/10gen/troubleshoot-mongoose/node_modules/mongoose/lib/query.js:2156:10)
    at Object.<anonymous> (/home/val/Workspace/10gen/troubleshoot-mongoose/gh-1959.js:10:37)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)
    at Function.Module._load (module.js:438:3)
    at Module.runMain (module.js:604:10)
  message: 'Cast to ObjectId failed for value "invalid" at path "_id"',
  name: 'CastError',
  kind: 'ObjectId',
  value: 'invalid',
  path: '_id',
  reason: undefined }
^C
$ 

Ah, was für ein Muppet ich gewesen bin! Natürlich damit umgehen, dass das Versprechen abgelehnt wird ... arg, mein Gehirn. Danke @vkarpov15

Rückrufe funktionieren auch, wenn Versprechungen zu viel Kopfzerbrechen bereiten MyModel.findOne({ _id: 'invalid' }).exec(error => console.error('error', error)); :)

VERWENDEN SIE NICHT ObjectId.isValid() , wenn es aus den obigen 12 Bytes, die Valeri gesagt hat, nicht bereits klar ist. Wurde gerade ziemlich gut von diesem verbrannt:
ObjectId.isValid('The Flagship') === true

@atcwells , wenn Sie Ihren hoch bewerteten Kommentar aktualisieren könnten, um diesen Teil aufzunehmen, denke ich, dass andere Leute es zu schätzen wissen, da ich es ursprünglich basierend auf dem getan habe, was Sie gesagt haben: ObjectId.isValid('The Flagship') === true

Der lustige Teil ist:
die folgende Anweisung gibt wahr zurück
mongoose.Types.ObjectId.isValid("Südafrika")

Was ich (vorerst) mache, ist die Überprüfung des Fehlertyps im Promise-Catch. Wenn es 'ObjectId' ist, bekomme ich 404 zurück. Ich gebe 404 zurück, weil die Ressource für den Verbraucher des API/Webdienstes nicht gefunden wurde oder nicht existiert.

Siehe Beispiel:

  Widget.findByIdAndRemove(resourceId)
    .then((result) => {
      if (!result) {
        let error = new Error('Resource not found');
        error.status = 404;
        next(error);
      } else {
        res.redirect(303, '/');
      }
    })
    .catch((error) => {
      if (error.kind === 'ObjectId') {
        let error = new Error('Resource not found');
        error.status = 404;

        next(error);
      } else {
        next(error);
      }
    });

AKTUALISIEREN:
Anstatt dies jedem Routenhandler im Controller hinzuzufügen. Ich füge dem globalen Catch-All-Handler hinzu.

Siehe Beispiel:

app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  if (err.kind === 'ObjectId') {
    err.status = 404;
  }

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

Ist es möglich, im Schema zu validieren? Ist keine bewährte Methode, wiederholen Sie das, wenn, aber ich möchte das Cast-Fehlerereignis protokollieren.

Warum kann Mongoose die Regex /^[a-fA-F0-9]{24}$/ für isValid nicht implementieren, wenn es sich nur um MongoDB handelt. Das ist so verwirrend. Wir haben nur eine Stunde damit verbracht, das Problem zu debuggen, und es stellte sich als so dumm heraus, dass Sie es nie bemerken werden

Ich würde empfehlen, dieses npm-Paket https://www.npmjs.com/package/valid-objectid zu verwenden
Es funktioniert perfekt

Ich habe dafür einen Workaround erstellt:

function isObjectId(value) {
  try {
      const { ObjectId } = mongoose.Types;
      const asString = value.toString(); // value is either ObjectId or string or anything
      const asObjectId = new ObjectId(asString);
      const asStringifiedObjectId = asObjectId.toString();
      return asString === asStringifiedObjectId;
    } catch (error) {
      return false;
    }
}

@mstmustisnt Ich denke, das muss versucht/abgefangen werden, wenn der Wert "123" ist, wird dies einen Fehler auslösen.

Falls es jemandem hilft, habe ich eine Anpassung des obigen ObjectId.isValid() -Ansatzes vorgenommen. Weil ich in meinem Anwendungsfall in der Lage sein möchte, eine Ressource entweder über ihre ID oder ihren URL-Slug abzurufen, z.

GET /users/59f5e7257a92d900168ce49a ... oder ...
GET /users/andrew-shankie

... Ich habe festgestellt, dass dies in meinem Controller gut funktioniert:

  const { id } = req.params;

  const query = {
    $or: [{ slug: id }],
  };

  if (mongoose.Types.ObjectId.isValid(id)) query.$or.push({ _id: id });

  User.findOne(query)
    .exec((err, user) => { ... }

In diesem Fall ist eine 12-Byte-Zeichenfolge immer noch eine gültige Objekt-ID. Die Suche danach gibt einfach ein Array der Länge Null zurück, anstatt einen Fehler auszulösen. Und weil ich eine $or -Abfrage verwende, sucht sie dann nach dem URL-Slug (meine andere mögliche Option).

Ist vielleicht nicht die eleganteste Lösung, aber bei mir funktioniert es.

@victorbadila ja genau. Ich habe nur einen Hinweis gegeben. Meinen Kommentar bearbeitet, platziert eigentlich nur den Code, den ich tatsächlich verwende.

validator.js hat eine eingebaute isMongoId Methode

Wann immer ich Mungo verwende, erweitere ich es immer mit ein paar statischen Hilfsmethoden:

const mongoose = require('mongoose');
const {Types: {ObjectId}} = mongoose;

//Helper to check if an ID is an object ID
mongoose.isObjectId = function(id) {
  return (id instanceof ObjectId);
};

//Helper to validate a string as object ID
mongoose.isValidObjectId = function(str) {
  if (typeof str !== 'string') {
    return false;
  }
  return str.match(/^[a-f\d]{24}$/i);
};

Sie können dies als Teil Ihrer Datenbankinitialisierungsskripts ausführen, sodass die Methoden immer in Ihrer App verfügbar sind.

Wenn ObjectId.isValid(id) wahr ist, können wir den Wert und die ID von (new ObjectId(id).toString()) beurteilen.

const mongoose = require('mongoose');
const {Types: {ObjectId}} = mongoose;
const validateObjectId = (id) => ObjectId.isValid(id) && (new ObjectId(id)).toString() === id;
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen