Mongoose: Cara terbaik untuk memvalidasi ObjectId

Dibuat pada 12 Mar 2014  ·  29Komentar  ·  Sumber: Automattic/mongoose

Saya memasukkan beberapa objectIds sebagai referensi dan saya ingin memastikan bahwa itu adalah objectid yang valid (saat ini saya mengalami crash ketika mereka bukan id yang valid).

Saya memeriksa untuk melihat apakah mereka merujuk ke objek yang valid hanya dengan melakukan panggilan find().

Bagaimana saya harus memeriksa apakah string dalam format yang benar? Saya telah melihat beberapa regex yang memeriksa apakah string adalah string 24 karakter dan hal-hal seperti itu - sepertinya retasan. Apakah ada validator internal pada ObjectId? Saya tidak tahu bagaimana menggunakannya. Terima kasih banyak!

Komentar yang paling membantu

Ini bekerja untuk saya:

var luwak = membutuhkan('./node_modules/luwak');
console.log(luwak.Jenis.ObjectId.isValid);
// [Fungsi: isValid]
console.log(luwak.Jenis.ObjectId.isValid('53cb6b9b4f4ddef1ad47f943'));
// benar
console.log(luwak.Tipe.ObjectId.isValid('bleurgh'));
// Salah

Semua 29 komentar

Saya mempunyai masalah yang sama. Saya mencoba mongoose.Schema.ObjectId.isValid() serta mongoose.Types.ObjectId.isValid() tetapi tidak satu pun dari properti tersebut yang memiliki metode isValid. Bagaimana Anda akhirnya memecahkan masalah ini? Saya juga melihat mongodb memiliki satu dan ada juga regex sebagai opsi lain. Saya lebih suka untuk tidak menggunakan regex atau harus membutuhkan('mongodb')

Ini bekerja untuk saya:

var luwak = membutuhkan('./node_modules/luwak');
console.log(luwak.Jenis.ObjectId.isValid);
// [Fungsi: isValid]
console.log(luwak.Jenis.ObjectId.isValid('53cb6b9b4f4ddef1ad47f943'));
// benar
console.log(luwak.Tipe.ObjectId.isValid('bleurgh'));
// Salah

Kebetulan metode di mongodb adalah metode bson lib, yang hanya memeriksa bukan null, 12 atau 24 char hex string - ini adalah regex seperti ini:

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

jadi ga terlalu ngehack kalo dipakai disana

sValid() selalu mengembalikan True jika string berisi 12 huruf

console.log(luwak.Jenis.ObjectId.isValid("zzzzzzzzzzzz")); // benar

Karena "zzzzzzzzzzzz" secara teknis adalah ObjectId yang valid - satu-satunya fitur yang menentukan id objek adalah panjangnya 12 byte. Lihat mongodb/js-bson#112. String JS dengan panjang 12 memiliki 12 byte, keanehan modulo unicode. Jika Anda ingin memeriksa string hex panjang 24, cukup periksa string yang cocok /^[a-fA-F0-9]{24}$/

"zzzzzzzzzzzz" bukan ObjectId yang valid. Misalnya daftar shell Mongo (versi 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")

Karena konstruktor ObjectId shell mongodb ditulis sedemikian rupa sehingga hanya menerima string hex. Ini adalah batasan di mongo Shell untuk kenyamanan, bukan dengan ObjectId tipe BSON. Memang ini adalah kasus yang agak berlawanan dengan intuisi mengingat bahwa string hex adalah bagaimana ObjectIds biasanya diwakili, tetapi jika Anda tidak menyukainya maka gunakan saja regex /^[a-fA-F0-9]{24}$/ :)

Mengapa kita mendapatkan false ketika kita mencoba melakukan isValid pada ObjectId itu sendiri dan bukan String? Bukankah seharusnya ini mengembalikan true karena dan ObjectId adalah ObjectId yang valid? Itu tidak masuk akal -- mungkin panggil .toString() jika itu adalah objek yang diteruskan ke isValid ?

@niftylettuce komentar diterima di #3365. Saat ini kami hanya menunda ke fungsi ObjectId.isValid() paket bson , yang tidak sejalan dengan cara orang berpikir tentang ObjectIds di luwak. Saya akan membuka PR untuk mengembalikan true jika Anda diberi ObjectId, itu tampaknya sangat masuk akal.

Kembali ke sedikit masalah lama di sini ... solusi @atcwells mongoose.Types.ObjectId.isValid('53cb6b9b4f4ddef1ad47f943') bekerja cukup baik untuk saya, tetapi tampaknya masih agak sulit untuk meminta pengontrol saya memeriksa apakah ID objek valid – kapan tentunya ini adalah kasus penggunaan yang cukup umum untuk ingin dapat mengirim ID yang salah ke server dan tidak membuatnya crash.

Idealnya, itu hanya akan mengembalikan sesuatu di err dalam panggilan balik sehingga kami dapat menanganinya dengan benar dan mengirim status HTTP yang benar menggunakan pengontrol kami.

Apakah ada kasus penggunaan di mana ini tidak akan menjadi fungsionalitas yang berguna di inti? Jika tidak, mungkin kita bisa membuat plugin. Saya telah melakukan pencarian cepat dan sepertinya tidak ada yang berhasil - https://github.com/CampbellSoftwareSolutions/mongoose-id-validator adalah untuk memvalidasi bahwa ID itu benar-benar ada, yang bukan itu yang ingin kami lakukan di sini – kami hanya ingin memastikan bahwa kami tidak menghasilkan kesalahan yang tidak tertangkap.

Saat ini, di pengontrol Express saya, setiap kali saya mengajukan permintaan yang memiliki ObjectId di dalamnya, misalnya DAPATKAN ke https://myproject/organisations/ {id}, saya harus melakukan sesuatu seperti:

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

... sebelum itu melanjutkan untuk melakukan Organisation.findOne();

Tampaknya cukup boilerplate. Saya senang menulis plugin atau sesuatu jika seseorang dapat mengarahkan saya ke arah yang benar untuk memulai. Sepertinya bukan plugin karena sebenarnya bukan skema ...

@shankiesan Anda tidak perlu melakukan itu, luwak akan menolak janji kueri jika id tidak valid.

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

Keluaran:

$ 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, betapa bodohnya aku! Tentu saja, tangani janji yang ditolak... arg, otakku. Terima kasih @vkarpov15

Panggilan balik juga berfungsi jika janji terlalu memusingkan MyModel.findOne({ _id: 'invalid' }).exec(error => console.error('error', error)); :)

JANGAN GUNAKAN ObjectId.isValid() jika belum jelas dari 12 byte di atas hal yang dikatakan Valeri. Baru saja dibakar cukup bagus oleh yang ini:
ObjectId.isValid('The Flagship') === true

@atcwells jika Anda dapat memperbarui komentar Anda yang sangat disukai untuk memasukkan bagian itu, saya pikir orang lain mungkin menghargainya karena saya awalnya melakukannya berdasarkan apa yang Anda katakan: ObjectId.isValid('The Flagship') === true

Bagian yang lucu adalah:
pernyataan berikut kembali benar
luwak.Types.ObjectId.isValid("Afrika Selatan")

Apa yang saya lakukan (untuk saat ini) adalah memeriksa jenis kesalahan dalam tangkapan janji. Jika 'ObjectId' saya mengembalikan 404. Saya mengembalikan 404 karena kepada konsumen layanan API/Web sumber daya tidak ditemukan atau tidak ada.

Lihat contoh:

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

MEMPERBARUI:
Alih-alih menambahkan ini ke setiap penangan rute di controller. Saya menambahkan ke global catch all handler.

Lihat contoh:

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

Apakah mungkin memvalidasi dalam skema? Bukan praktik terbaik, ulangi jika, tetapi saya ingin mencatat peristiwa kesalahan pemain.

Mengapa luwak tidak dapat mengimplementasikan regex /^[a-fA-F0-9]{24}$/ untuk isValid ketika hanya berurusan dengan MongoDB. Ini sangat membingungkan. Kami hanya menghabiskan waktu satu jam untuk men-debug masalah tersebut dan ternyata hal tersebut menjadi hal yang konyol sehingga Anda tidak akan pernah menyadarinya

Saya akan merekomendasikan untuk menggunakan paket npm ini https://www.npmjs.com/package/valid-objectid
Ini bekerja dengan sempurna

Saya telah membuat solusi untuk ini:

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 Saya pikir itu perlu dicoba/ditangkap, jika nilainya "123" ini akan menimbulkan kesalahan.

Jika itu membantu siapa pun, saya telah melakukan adaptasi dari pendekatan ObjectId.isValid() di atas. Karena dalam kasus penggunaan saya, saya ingin bisa mendapatkan sumber daya baik dengan ID atau url-slug-nya, misalnya:

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

...Saya telah menemukan bahwa ini berfungsi dengan baik di pengontrol saya:

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

Dalam hal ini, string 12-byte masih merupakan ID Objek yang valid, mencarinya hanya mengembalikan array panjang nol, daripada membuat kesalahan. Dan karena saya menggunakan kueri $or , kueri itu kemudian mencari berdasarkan slug URL (opsi lain yang memungkinkan).

Mungkin bukan solusi yang paling elegan, tetapi itu berhasil untuk saya.

@victorbadila ya, tepatnya. Saya hanya memberi petunjuk. Mengedit komentar saya, sebenarnya hanya menempatkan kode yang sebenarnya saya gunakan.

validator.js memiliki metode isMongoId bawaan

Setiap kali saya menggunakan luwak, saya selalu memperluasnya dengan beberapa metode pembantu statis:

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

Anda dapat menjalankan ini sebagai bagian dari skrip inisialisasi database, sehingga metode selalu tersedia di aplikasi Anda.

Jika ObjectId.isValid(id) benar, kita dapat menilai (new ObjectId(id).toString()) nilai dan id.

const mongoose = require('mongoose');
const {Types: {ObjectId}} = mongoose;
const validateObjectId = (id) => ObjectId.isValid(id) && (new ObjectId(id)).toString() === id;
Apakah halaman ini membantu?
0 / 5 - 0 peringkat