Я вставляю некоторые идентификаторы объектов в качестве ссылок и хочу убедиться, что они являются действительными идентификаторами объектов (сейчас у меня происходит сбой, когда они не являются действительными идентификаторами).
Я проверяю, ссылаются ли они на действительные объекты, просто выполняя вызов find().
Как мне проверить правильность формата строк? Я видел некоторые регулярные выражения, которые проверяют, является ли строка строкой из 24 символов и тому подобное - похоже на взлом. Есть ли внутренний валидатор для ObjectId? Я не мог понять, как его использовать. Большое спасибо!
У меня такая же проблема. Я пробовал mongoose.Schema.ObjectId.isValid(), а также mongoose.Types.ObjectId.isValid(), но ни одно из этих свойств не имеет метода isValid. Как вы в итоге решили эту проблему? Я также вижу, что у mongodb есть один, а в качестве другого варианта есть регулярное выражение. Я бы предпочел не использовать регулярное выражение и не требовать ('mongodb')
Это работает для меня:
var mongoose = require('./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-символьной шестнадцатеричной строки — это регулярное выражение, подобное этому:
var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
так что это не может быть слишком хакерским, если оно используется там
sValid() всегда возвращает True, если строка содержит 12 букв.
console.log(mongoose.Types.ObjectId.isValid("zzzzzzzzzzzz")); // истинный
Поскольку «zzzzzzzzzzzz» технически является допустимым ObjectId, единственной определяющей особенностью идентификатора объекта является то, что его длина составляет 12 байт. См. mongodb/js-bson#112. Строка JS длиной 12 имеет 12 байтов по модулю странностей Юникода. Если вы хотите проверить шестнадцатеричную строку длиной 24, просто проверьте, соответствует ли строка /^[a-fA-F0-9]{24}$/
"zzzzzzzzzzzz" не является допустимым идентификатором объекта. Например, список оболочек Mongo (версия 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 для удобства, а не с ObjectId типа BSON. По общему признанию, это несколько нелогичный случай, учитывая, что шестнадцатеричные строки - это то, как обычно представляются ObjectId, но если вам это не нравится, просто используйте регулярное выражение /^[a-fA-F0-9]{24}$/
:)
Почему мы получаем false, когда пытаемся выполнить isValid
для самого ObjectId, а не для строки? Разве это не должно возвращать true, поскольку ObjectId является допустимым ObjectId? Это не имеет смысла — может быть, вызвать .toString()
, если это объект, который передается в isValid
?
Комментарии @niftylettuce приветствуются в #3365. Прямо сейчас мы просто полагаемся на функцию ObjectId.isValid() пакета bson , которая не совсем соответствует тому, как люди думают об ObjectId в mongoose. Я открою PR для возврата true, если вам дан ObjectId, хотя это кажется вполне разумным.
Возвращаясь к немного старой проблеме здесь ... Решение @atcwells mongoose.Types.ObjectId.isValid('53cb6b9b4f4ddef1ad47f943')
работает достаточно хорошо для меня, но все еще кажется немного хакерским, чтобы мой контроллер проверял, действителен ли идентификатор объекта - когда конечно, это довольно распространенный вариант использования, чтобы иметь возможность отправлять неправильно сформированный идентификатор на сервер и не допускать его сбоя.
В идеале он просто возвращает что-то в err
в обратном вызове, чтобы мы могли правильно обработать его и отправить правильный HTTP-статус с помощью нашего контроллера.
Есть ли вариант использования, когда это не было бы полезной функциональностью в ядре? Если нет, возможно, мы можем сделать плагин. У меня был быстрый поиск, и, похоже, нет ничего, что выполняло бы эту работу — https://github.com/CampbellSoftwareSolutions/mongoose-id-validator предназначено для проверки того, что идентификатор действительно существует, а это не то, что мы хотим сделать здесь - мы просто хотим убедиться, что мы не генерируем неперехваченную ошибку.
Прямо сейчас, в моем контроллере Express, каждый раз, когда я отправляю запрос, в котором есть ObjectId, например, GET для https://myproject/organisations/ {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
$
Кроме того, re: плагины http://thecodebarbarian.com/2015/03/06/guide-to-mongoose-plugins
Ах, какой я была куклой! Конечно, смириться с отказом от обещания... Арг, мой мозг. Спасибо @vkarpov15
Обратные вызовы тоже работают, если промисы — это слишком большая головная боль MyModel.findOne({ _id: 'invalid' }).exec(error => console.error('error', error));
:)
НЕ ИСПОЛЬЗУЙТЕ ObjectId.isValid()
, если это еще не ясно из приведенных выше 12 байтов, которые сказал Валери. Просто хорошо обожглась вот этим:
ObjectId.isValid('The Flagship') === true
@atcwells , если бы вы могли обновить свой высоко оцененный комментарий, включив в него эту часть, я думаю, что другие люди могли бы это оценить, поскольку я изначально делал это на основе того, что вы сказали: ObjectId.isValid('The Flagship') === true
Самое смешное:
следующее утверждение возвращает 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);
}
});
ОБНОВИТЬ:
Вместо того, чтобы добавлять это к каждому обработчику маршрута в контроллере. Я добавляю в глобальный обработчик catch all.
См. пример:
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');
});
Можно ли проверить в схеме? Не рекомендуется повторять это, если, но я хочу регистрировать событие ошибки приведения.
Почему мангуст не может реализовать регулярное выражение /^[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
Всякий раз, когда я использую mongoose, я всегда расширяю его несколькими статическими вспомогательными методами:
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) истинно, мы можем судить о значении и идентификаторе (new ObjectId(id).toString()) .
const mongoose = require('mongoose');
const {Types: {ObjectId}} = mongoose;
const validateObjectId = (id) => ObjectId.isValid(id) && (new ObjectId(id)).toString() === id;
Самый полезный комментарий
Это работает для меня:
var mongoose = require('./node_modules/mongoose');
console.log(mongoose.Types.ObjectId.isValid);
// [Функция: допустима]
console.log(mongoose.Types.ObjectId.isValid('53cb6b9b4f4ddef1ad47f943'));
// истинный
console.log(mongoose.Types.ObjectId.isValid('bleurgh'));
// ложный