当从数据库本身获取 modelObject 时,modelObject.save() 是否只更新现有的数据库文档?
Does modelObject.save() only update an existing database document when the modelObject was obtained from the database itself?
要使用演示问题的示例,假设我有一个由以下架构定义的用户模型:
var UserSchema = new Schema({
username: String,
email: String
}
mongoose.model('User', UserSchema);
我知道要使用保存方法更新用户,我可以查询用户然后保存更改,如下所示:
User.findOne({username: usernameTofind}, function(err, user) {
//ignore errors for brevity
user.email = newEmail;
user.save(function(err) { console.log('User email updated') });
});
但是,如果我尝试创建一个具有完全相同字段值(包括 _id)的新用户对象,是否有可能覆盖数据库文档?我假设不会,因为理论上这意味着恶意用户可以利用不安全的 api 并覆盖现有文档(例如使用 'Create a New Account' 请求,这不会't/couldn'依赖于已经通过身份验证的用户),但更重要的是,当我尝试使用请求工具执行此操作时(我正在使用 Postman,但我确信类似的 curl 命令就足够了),我得到一个重复的 _id 错误
MongoError: insertDocument :: caused by :: 11000 E11000 duplicate key error index
所以我只是想澄清一下,更新现有文档的唯一方法是查询文档,修改返回的实例,然后调用该实例的保存方法,或者使用静态 update()。这两者都可以通过要求身份验证来保护。
如果有帮助的话,上面提到了我提出这个问题的动机,因为我想确保用户在公开公开以下方法时无法覆盖现有文档:
userCtrl.create = function(req, res, next) {
var user = new User(req.body);
user.save(function(err) {
if (err) {
return next(err);
} else {
res.json(user);
}
});
};
快速编辑:我刚刚意识到,如果是这种情况,那么数据库如何知道查询的实例和具有完全相同的键和属性的新用户对象之间的区别?
Does modelObject.save()
only update an existing database document when
the modelObject was obtained from the database itself?
是的,确实如此。有一个标志指示文档是否是新的。如果是新的,Mongoose 将插入文档。如果没有,那么它将更新文档。标志是 Document#isNew
.
当您 find
一个文档时:
User.findOne({username: usernameTofind}, function(err, user) {
//ignore errors for brevity
console.log(user.isNew) // => will return false
});
创建新实例时:
var user = new User(req.body);
console.log(user.isNew) // => will return true
So I just want to clarify that the only way to update an existing
document is to query for the document, modify the returned instance,
then call the save method on that instance, OR use the static
update()
. Both of these could be secured by requiring authentication.
还有其他方法可以更新文档,使用 Model#update
, Model.findOneAndUpdate
and others。
但是,您无法更新 _id
字段。 MongoDB 即使 Mongoose 尚未发出正确的数据库命令也不会允许。如果你尝试它,你会得到类似这样的错误:
The _id field cannot be changed from {_id: ObjectId('550d93cbaf1e9abd03bf0ad2')} to {_id: ObjectId('550d93cbaf1e9abd03bf0ad3')}.
但假设您使用问题中的最后一段代码来创建新用户,Mongoose 将发出 insert
命令,因此有人无法覆盖现有文档。即使它在请求正文中传递了一个 _id
字段,MongoDB 也会抛出一个 E11000 duplicate key error index
错误。
此外,在使用用户创建用户之前,您应该过滤用户可以作为负载传递的字段。例如,您可以创建一个通用函数来接收一个对象和一组允许的参数:
module.exports = function(object, allowedParams) {
return Object.keys(object).reduce(function(newObject, param) {
if (allowedParams.indexOf(param) !== -1)
newObject[param] = object[param];
return newObject;
}, {});
}
然后你只需要和使用函数来过滤请求体:
var allow = require('./parameter-filter');
function filter(params) {
return allow(params, ["username", "email"]);
}
var user = new User(filter(req.body));
要使用演示问题的示例,假设我有一个由以下架构定义的用户模型:
var UserSchema = new Schema({
username: String,
email: String
}
mongoose.model('User', UserSchema);
我知道要使用保存方法更新用户,我可以查询用户然后保存更改,如下所示:
User.findOne({username: usernameTofind}, function(err, user) {
//ignore errors for brevity
user.email = newEmail;
user.save(function(err) { console.log('User email updated') });
});
但是,如果我尝试创建一个具有完全相同字段值(包括 _id)的新用户对象,是否有可能覆盖数据库文档?我假设不会,因为理论上这意味着恶意用户可以利用不安全的 api 并覆盖现有文档(例如使用 'Create a New Account' 请求,这不会't/couldn'依赖于已经通过身份验证的用户),但更重要的是,当我尝试使用请求工具执行此操作时(我正在使用 Postman,但我确信类似的 curl 命令就足够了),我得到一个重复的 _id 错误
MongoError: insertDocument :: caused by :: 11000 E11000 duplicate key error index
所以我只是想澄清一下,更新现有文档的唯一方法是查询文档,修改返回的实例,然后调用该实例的保存方法,或者使用静态 update()。这两者都可以通过要求身份验证来保护。
如果有帮助的话,上面提到了我提出这个问题的动机,因为我想确保用户在公开公开以下方法时无法覆盖现有文档:
userCtrl.create = function(req, res, next) {
var user = new User(req.body);
user.save(function(err) {
if (err) {
return next(err);
} else {
res.json(user);
}
});
};
快速编辑:我刚刚意识到,如果是这种情况,那么数据库如何知道查询的实例和具有完全相同的键和属性的新用户对象之间的区别?
Does
modelObject.save()
only update an existing database document when the modelObject was obtained from the database itself?
是的,确实如此。有一个标志指示文档是否是新的。如果是新的,Mongoose 将插入文档。如果没有,那么它将更新文档。标志是 Document#isNew
.
当您 find
一个文档时:
User.findOne({username: usernameTofind}, function(err, user) {
//ignore errors for brevity
console.log(user.isNew) // => will return false
});
创建新实例时:
var user = new User(req.body);
console.log(user.isNew) // => will return true
So I just want to clarify that the only way to update an existing document is to query for the document, modify the returned instance, then call the save method on that instance, OR use the static
update()
. Both of these could be secured by requiring authentication.
还有其他方法可以更新文档,使用 Model#update
, Model.findOneAndUpdate
and others。
但是,您无法更新 _id
字段。 MongoDB 即使 Mongoose 尚未发出正确的数据库命令也不会允许。如果你尝试它,你会得到类似这样的错误:
The _id field cannot be changed from {_id: ObjectId('550d93cbaf1e9abd03bf0ad2')} to {_id: ObjectId('550d93cbaf1e9abd03bf0ad3')}.
但假设您使用问题中的最后一段代码来创建新用户,Mongoose 将发出 insert
命令,因此有人无法覆盖现有文档。即使它在请求正文中传递了一个 _id
字段,MongoDB 也会抛出一个 E11000 duplicate key error index
错误。
此外,在使用用户创建用户之前,您应该过滤用户可以作为负载传递的字段。例如,您可以创建一个通用函数来接收一个对象和一组允许的参数:
module.exports = function(object, allowedParams) {
return Object.keys(object).reduce(function(newObject, param) {
if (allowedParams.indexOf(param) !== -1)
newObject[param] = object[param];
return newObject;
}, {});
}
然后你只需要和使用函数来过滤请求体:
var allow = require('./parameter-filter');
function filter(params) {
return allow(params, ["username", "email"]);
}
var user = new User(filter(req.body));