我正在插入一些 objectIds 作为引用,并且我想确保它们是有效的 objectids(现在当它们不是有效的 ids 时我会崩溃)。
我正在检查它们是否通过调用 find() 来引用有效对象。
我应该如何检查字符串的格式是否正确? 我见过一些正则表达式检查字符串是否是 24 个字符的字符串和类似的东西 - 似乎是一个 hack。 ObjectId 上是否有内部验证器? 我无法弄清楚如何使用它。 非常感谢!
我有同样的问题。 我尝试了 mongoose.Schema.ObjectId.isValid() 和 mongoose.Types.ObjectId.isValid() 但这些属性都没有 isValid 方法。 你最终是如何解决这个问题的? 我还看到 mongodb 有一个,还有正则表达式作为另一种选择。 我宁愿不使用正则表达式也不必 require('mongodb')
这对我有用:
var mongoose = require('./node_modules/mongoose');
console.log(mongoose.Types.ObjectId.isValid);
// [函数: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}$");
所以如果在那里使用它就不会太hacky
如果字符串包含 12 个字母,sValid() 总是返回 True
console.log(mongoose.Types.ObjectId.isValid("zzzzzzzzzzzz")); // 真的
因为“zzzzzzzzzzzz”在技术上是一个有效的 ObjectId - 一个对象 id 的唯一定义特征是它的 12 字节长。 见 mongodb/js-bson#112。 长度为 12 的 JS 字符串有 12 个字节,以 unicode 为模。 如果要检查长度为 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")
因为 mongodb shell 的 ObjectId 构造函数是这样编写的,它只接受十六进制字符串。 为了方便起见,这是 mongo shell 中的一个限制,而不是 BSON 类型的 ObjectId。 诚然,这是一个有点违反直觉的情况,因为十六进制字符串通常是 ObjectIds 的表示方式,但如果你不喜欢它,那么只需使用正则表达式/^[a-fA-F0-9]{24}$/
:)
当我们尝试对 ObjectId 本身而不是 String 执行isValid
时,为什么我们会得到错误? 既然 ObjectId 是有效的 ObjectId,这不应该返回 true 吗? 这没有意义——如果.toString()
是一个传递给isValid
的对象,可能会调用它?
@niftylettuce欢迎在 #3365 发表评论。 现在我们只是推迟到bson 包的 ObjectId.isValid() 函数,它与人们对 mongoose 中 ObjectIds 的看法并不完全一致。 如果你得到一个 ObjectId,我会打开一个 PR 来返回 true,这似乎是完全合理的。
回到这里的一个老问题...... @atcwells解决方案mongoose.Types.ObjectId.isValid('53cb6b9b4f4ddef1ad47f943')
对我来说已经足够好了,但是让我的控制器检查对象 ID 是否有效似乎仍然有点笨拙——什么时候当然,希望能够将格式错误的 ID 发送到服务器而不让它崩溃是一个非常常见的用例。
理想情况下,它会简单地在回调中的err
中返回一些内容,以便我们可以正确处理它并使用我们的控制器发送正确的 HTTP 状态。
是否有一个用例在核心中这不是有用的功能? 如果没有,也许我们可以制作一个插件。 我进行了快速搜索,似乎没有任何东西可以完成这项工作 - https://github.com/CampbellSoftwareSolutions/mongoose-id-validator用于验证 ID 是否确实存在,这不是什么我们想在这里做——我们只是想确保我们不会产生未捕获的错误。
现在,在我的 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 ,您不需要这样做,如果 id 无效,猫鼬将拒绝查询承诺。
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
$
啊,我真是个布偶! 当然,处理被拒绝的承诺...... arg,我的大脑。 谢谢@vkarpov15
如果 promise 太让人头疼的话,回调也可以工作MyModel.findOne({ _id: 'invalid' }).exec(error => console.error('error', error));
:)
如果 Valeri 所说的上述 12 个字节的内容尚不清楚,请不要使用ObjectId.isValid()
。 刚刚被这个烧得很好:
ObjectId.isValid('The Flagship') === true
@atcwells如果您可以更新您高度赞成的评论以包括那一点,我认为其他人可能会欣赏它,因为我最初是根据您所说的: ObjectId.isValid('The Flagship') === true
有趣的部分是:
以下语句返回 true
mongoose.Types.ObjectId.isValid("南非")
我(目前)正在使用的是检查 promise catch 中的错误类型。 如果是“ObjectId”,我会返回 404。我会返回 404,因为对于 API/Web 服务的使用者而言,资源未找到或不存在。
参见示例:
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 在只处理 MongoDB 时不能实现 isValid 的正则表达式 /^[a-fA-F0-9]{24}$/ 。 这太令人困惑了。 我们只花了一个小时调试这个问题,结果发现这是一个你永远不会注意到的愚蠢的事情
我建议使用这个 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()
方法进行改编。 因为在我的用例中,我希望能够通过其 ID 或其 url-slug 获取资源,例如:
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 字节的字符串仍然是一个有效的对象 ID,搜索它只会返回一个长度为零的数组,而不是抛出错误。 而且因为我使用的是$or
查询,所以它会通过 URL slug 进行搜索(我的其他可能选项)。
可能不是最优雅的解决方案,但它对我有用。
@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) 为真,我们可以判断 (new ObjectId(id).toString()) 的值和id。
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);
// [函数:isValid]
console.log(mongoose.Types.ObjectId.isValid('53cb6b9b4f4ddef1ad47f943'));
// 真的
console.log(mongoose.Types.ObjectId.isValid('bleurgh'));
// 错误的