Mongoose: أفضل طريقة للتحقق من صحة ObjectId

تم إنشاؤها على ١٢ مارس ٢٠١٤  ·  29تعليقات  ·  مصدر: Automattic/mongoose

أقوم بإدخال بعض العناصر كمراجع وأريد التأكد من أنها كائنات صالحة (الآن أتلقى تعطلاً عندما لا تكون معرفات صالحة).

أنا أتحقق لمعرفة ما إذا كانت تشير إلى كائنات صالحة بمجرد إجراء مكالمة بحث ().

كيف يمكنني التحقق مما إذا كانت السلاسل بالتنسيق الصحيح؟ لقد رأيت بعض regexs التي تتحقق مما إذا كانت السلسلة عبارة عن سلسلة مكونة من 24 حرفًا وأشياء من هذا القبيل - تبدو وكأنها اختراق. هل يوجد مدقق داخلي في ObjectId؟ لم أستطع معرفة كيفية استخدامه. شكرا جزيلا!

التعليق الأكثر فائدة

هذا يناسبني:

var mongoose = يتطلب ('./ node_modules / mongoose') ؛
console.log (mongoose.Types.ObjectId.isValid) ،
// [الوظيفة: صالحة]
console.log (mongoose.Types.ObjectId.isValid ('53cb6b9b4f4ddef1ad47f943')) ،
// صحيح
console.log (mongoose.Types.ObjectId.isValid ('bleurgh')) ؛
// خاطئة

ال 29 كومينتر

انا لدى نفس المشكله. لقد جربت mongoose.Schema.ObjectId.isValid () بالإضافة إلى mongoose.Types.ObjectId.isValid () ولكن أيا من هذه الخاصيتين ليس لهما طريقة isValid. كيف انتهى بك الأمر إلى حل هذه المشكلة؟ أرى أيضًا أن mongodb لديه واحد وهناك أيضًا regex كخيار آخر. أفضل عدم استخدام regex ولا يجب أن أطلب ("mongodb")

هذا يناسبني:

var mongoose = يتطلب ('./ node_modules / mongoose') ؛
console.log (mongoose.Types.ObjectId.isValid) ،
// [الوظيفة: صالحة]
console.log (mongoose.Types.ObjectId.isValid ('53cb6b9b4f4ddef1ad47f943')) ،
// صحيح
console.log (mongoose.Types.ObjectId.isValid ('bleurgh')) ؛
// خاطئة

بالمناسبة ، الطريقة في mongodb هي طريقة bson lib ، والتي تتحقق فقط من عدم وجود سلسلة سداسية عشرية 12 أو 24 حرفًا - إنها regex مثل هذا:

var checkForHexRegExp = RegExp جديد ("^ [0-9a-fA-F] {24} $") ؛

لذلك لا يمكن أن يكون اختراقًا شديدًا إذا تم استخدامه هناك

تقوم sValid () دائمًا بإرجاع True إذا كانت السلسلة تحتوي على 12 حرفًا

console.log (mongoose.Types.ObjectId.isValid ("zzzzzzzzzzzz")) ؛ // صحيح

نظرًا لأن "zzzzzzzzzzzz" هو معرف كائن صالح من الناحية الفنية - فإن السمة المحددة الوحيدة لمعرف الكائن هي أن طوله يبلغ 12 بايت. انظر mongodb / js-bson # 112. سلسلة JS بطول 12 بها 12 بايت ، وهي وحدات غريبة أحادية الشفرة. إذا كنت تريد البحث عن سلسلة سداسية عشرية طولها 24 ، فقط تحقق من السلسلة التي تطابق /^[a-fA-F0-9]{24}$/

"zzzzzzzzzzzz" ليس ObjectId صالحًا. على سبيل المثال Mongo shell listiong (إصدار mongodb - 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")

لأن مُنشئ ObjectId الخاص بصدفة mongodb مكتوب بحيث لا يقبل إلا السلاسل السداسية. إنه قيد في mongo shell للراحة ، وليس مع BSON type ObjectId. من المسلم به أن هذه حالة غير بديهية إلى حد ما نظرًا لأن السلاسل السداسية هي الطريقة التي يتم بها تمثيل ObjectIds عادةً ، ولكن إذا لم تعجبك ، فما عليك سوى استخدام regex /^[a-fA-F0-9]{24}$/ :)

لماذا نخطئ عندما نحاول تنفيذ isValid على ObjectId نفسه وليس على String؟ ألا يجب أن يعود هذا صحيحًا لأن ومعرّف الكائن هو معرف كائن صالح؟ هذا غير منطقي - ربما استدعاء .toString() إذا كان كائنًا يتم تمريره إلى isValid ؟

niftylettuce التعليقات مرحب بها في # 3365. في الوقت الحالي ، نرجئ فقط إلى دالة ObjectId.isValid () الخاصة بحزمة bson ، والتي لا تتوافق تمامًا مع طريقة تفكير الأشخاص في ObjectIds في النمس. سأقوم بفتح العلاقات العامة للعودة إلى الحقيقة إذا أعطيت ObjectId رغم ذلك ، فهذا يبدو معقولاً تمامًا.

بالعودة إلى مشكلة قديمة هنا ... حل atcwells mongoose.Types.ObjectId.isValid('53cb6b9b4f4ddef1ad47f943') يعمل جيدًا بما يكفي بالنسبة لي ، ولكن لا يزال من الصعب بعض الشيء أن تضطر إلى جعل وحدة التحكم الخاصة بي تتحقق مما إذا كان معرف الكائن صالحًا - متى من المؤكد أنها حالة استخدام شائعة جدًا حيث تريد أن تكون قادرًا على إرسال معرف تم تشكيله بشكل غير صحيح إلى الخادم وعدم تعطله.

من الناحية المثالية ، سيعيد شيئًا ما في err في رد الاتصال حتى نتمكن من التعامل معه بشكل صحيح وإرسال حالة HTTP الصحيحة باستخدام وحدة التحكم الخاصة بنا.

هل هناك حالة استخدام حيث لا تكون هذه الوظيفة مفيدة في جوهرها؟ إذا لم يكن الأمر كذلك ، فربما يمكننا إنشاء مكون إضافي. لقد أجريت بحثًا سريعًا ولا يبدو أن هناك أي شيء يؤدي المهمة - https://github.com/CampbellSoftwareSolutions/mongoose-id-validator مخصص للتحقق من وجود المعرف بالفعل ، وهذا ليس ما نريد أن نفعل هنا - نريد ببساطة أن نتأكد من أننا لا ننتج خطأ غير محقق.

في الوقت الحالي ، في وحدة التحكم Express الخاصة بي ، في كل مرة أتقدم بطلب يحتوي على ObjectId ، على سبيل المثال GET إلى https: // myproject / members / {id} ، يجب أن أفعل شيئًا مثل:

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

... قبل ذلك انتقل إلى القيام بـ Organisation.findOne();

يبدو نموذجي جدا. يسعدني كتابة مكون إضافي أو شيء ما إذا كان بإمكان شخص ما توجيهي في الاتجاه الصحيح من أين أبدأ. لا يبدو وكأنه مكون إضافي لأنه ليس شيئًا مخططًا حقًا ...

shankiesan ، لست مضطرًا للقيام بذلك ، سيرفض النمس وعد الاستعلام إذا كان المعرف غير صالح.

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

انتاج:

$ 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
$ 

أيضًا ، أعد: المكونات الإضافية http://thecodebarbarian.com/2015/03/06/guide-to-mongoose-plugins

آه يا ​​لها من دمية! بالطبع ، تعامل مع الوعد المرفوض ... arg ، عقلي. شكرا @ vkarpov15

تعمل عمليات رد الاتصال أيضًا إذا كانت الوعود تمثل صداعًا كبيرًا MyModel.findOne({ _id: 'invalid' }).exec(error => console.error('error', error)); :)

لا تستخدم ObjectId.isValid() إذا لم يكن واضحًا بالفعل من 12 بايت أعلاه الشيء الذي قاله Valeri. لقد أحرقت للتو جيدًا من خلال هذا:
ObjectId.isValid('The Flagship') === true

atcwells إذا كان بإمكانك تحديث تعليقك الذي تم التصويت عليه بدرجة عالية لتضمين ذلك الجزء ، أعتقد أن الآخرين قد يقدرونه لأنني كنت أفعل ذلك في البداية بناءً على ما قلته: ObjectId.isValid('The Flagship') === true

الجزء المضحك هو:
العبارة التالية تعود صحيحًا
mongoose.Types.ObjectId.isValid ("جنوب أفريقيا")

ما أتناوله (في الوقت الحالي) هو التحقق من نوع الخطأ في التقاط الوعد. إذا كان "ObjectId" يتم إرجاع 404. سأقوم بإرجاع 404 لأنه لم يتم العثور على المورد أو أنه غير موجود لمستهلك API / خدمة الويب.

انظر المثال:

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

تحديث:
بدلاً من إضافة هذا إلى كل معالج مسار في وحدة التحكم. أنا أضيف إلى المصيد الشامل لمعالج جميع.

انظر المثال:

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

هل من الممكن التحقق من صحة في المخطط؟ ليس من أفضل الممارسات كرر ذلك إذا ، لكني أريد تسجيل حدث خطأ الإرسال.

لماذا لا يستطيع mongoose تنفيذ regex / ^ [a-fA-F0-9] {24} $ / لـ isValid عندما يتعامل فقط مع MongoDB. هذا محير جدا. لقد أمضينا ساعة فقط في تصحيح المشكلة واتضح أنها شيء سخيف لن تلاحظه أبدًا

أوصي باستخدام حزمة npm هذه https://www.npmjs.com/package/valid-objectid
إنه يعمل بشكل مثالي

لقد أنشأت حلاً لهذا:

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 أعتقد أنه يجب المحاولة / الإمساك بها ، إذا كانت القيمة "123" ، فسيؤدي ذلك إلى حدوث خطأ.

في حال كان ذلك يساعد أي شخص ، فقد كنت أقوم بتعديل نهج ObjectId.isValid() أعلاه. لأنه في حالة الاستخدام الخاصة بي ، أريد أن أكون قادرًا على الحصول على مورد إما عن طريق معرفه أو رابط عنوان url الخاص به ، على سبيل المثال:

GET /users/59f5e7257a92d900168ce49a ... أو ...
GET /users/andrew-shankie

... لقد وجدت أن هذا يعمل بشكل جيد في وحدة التحكم الخاصة بي:

  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) => { ... }

في هذه الحالة ، لا تزال السلسلة المكونة من 12 بايت معرف كائن صالحًا ، حيث يؤدي البحث عنها ببساطة إلى إرجاع مصفوفة ذات طول صفري ، بدلاً من إلقاء خطأ. ولأنني أستخدم استعلام $or ، فإنه يبحث بعد ذلك عن طريق الرابط الثابت لعنوان URL (خياري الآخر المحتمل).

قد لا يكون الحل الأكثر أناقة ، لكنه يناسبني.

victorbadila نعم بالضبط. أنا فقط أعطيت تلميحا. قمت بتحرير تعليقي ، في الواقع فقط وضع الكود الذي أستخدمه بالفعل.

يحتوي Validator.js على طريقة isMongoId مضمنة

عندما أستخدم النمس ، أقوم دائمًا بتمديده ببعض الطرق المساعدة الثابتة:

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

يمكنك تشغيل هذا كجزء من البرامج النصية لتهيئة قاعدة البيانات الخاصة بك ، لذلك تكون الأساليب متاحة دائمًا في تطبيقك.

إذا كان ObjectId.isValid (id) صحيحًا ، فيمكننا الحكم على (معرف كائن جديد (معرف) .toString ()) ومعرف.

const mongoose = require('mongoose');
const {Types: {ObjectId}} = mongoose;
const validateObjectId = (id) => ObjectId.isValid(id) && (new ObjectId(id)).toString() === id;
هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات