編集したデータをマングースに保存する前に検証を行う方法は?
たとえば、sample.name
はすでにデータベースに存在します。ユーザーは何らかのエラーを受け取ります。そのようなものです。以下に私のコードを示します
//Post: /sample/edit
app.post(uri + '/edit', function (req, res, next) {
Sample.findById(req.param('sid'), function (err, sample) {
if (err) {
return next(new Error(err));
}
if (!sample) {
return next(new Error('Invalid reference to sample information'));
}
// basic info
sample.name = req.body.supplier.name;
sample.tin = req.body.supplier.tin;
// contact info
sample.contact.email = req.body.supplier.contact.email;
sample.contact.mobile = req.body.supplier.contact.mobile;
sample.contact.landline = req.body.supplier.contact.landline;
sample.contact.fax = req.body.supplier.contact.fax;
// address info
sample.address.street = req.body.supplier.address.street;
sample.address.city = req.body.supplier.address.city;
sample.address.state = req.body.supplier.address.state;
sample.address.country = req.body.supplier.address.country;
sample.address.Zip = req.body.supplier.address.Zip;
sample.save(function (err) {
if (err) {
return next(new Error(err));
}
res.redirect(uri + '/view/' + sample._id);
});
});
});
通常は mongoose validation を使用できますが、非同期の結果(既存の名前に対するdbクエリ)が必要であり、バリデーターはpromiseをサポートしていないため(私が知る限り)、独自の関数を作成する必要がありますコールバックを渡します。以下に例を示します。
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
mongoose.connect('mongodb://localhost/testDB');
var UserSchema = new Schema({
name: {type:String}
});
var UserModel = mongoose.model('UserModel',UserSchema);
function updateUser(user,cb){
UserModel.find({name : user.name}, function (err, docs) {
if (docs.length){
cb('Name exists already',null);
}else{
user.save(function(err){
cb(err,user);
});
}
});
}
UserModel.findById(req.param('sid'),function(err,existingUser){
if (!err && existingUser){
existingUser.name = 'Kevin';
updateUser(existingUser,function(err2,user){
if (err2 || !user){
console.log('error updated user: ',err2);
}else{
console.log('user updated: ',user);
}
});
}
});
更新:より良い方法
Preフックは保存を停止するより自然な場所のようです:
UserSchema.pre('save', function (next) {
var self = this;
UserModel.find({name : self.name}, function (err, docs) {
if (!docs.length){
next();
}else{
console.log('user exists: ',self.name);
next(new Error("User exists!"));
}
});
}) ;
更新2:非同期カスタムバリデーター
Mongooseは現在、非同期カスタムバリデータをサポートしているように見えるので、おそらく自然な解決策でしょう。
var userSchema = new Schema({
name: {
type: String,
validate: {
validator: function(v, cb) {
User.find({name: v}, function(err,docs){
cb(docs.length == 0);
});
},
message: 'User already exists!'
}
}
});
使用される@nfreezeの例を続行する別の方法は、この検証方法です。
UserModel.schema.path('name').validate(function (value, res) {
UserModel.findOne({name: value}, 'id', function(err, user) {
if (err) return res(err);
if (user) return res(false);
res(true);
});
}, 'already exists');
少ないコードでこれを実現する別の方法を次に示します。
更新3:非同期モデルクラスの静的
オプション2と同様に、これにより、スキーマに直接リンクされているが、モデルを使用して同じファイルから呼び出される関数を作成できます。
model.js
userSchema.statics.updateUser = function(user, cb) {
UserModel.find({name : user.name}).exec(function(err, docs) {
if (docs.length){
cb('Name exists already', null);
} else {
user.save(function(err) {
cb(err,user);
}
}
});
}
ファイルからの呼び出し
var User = require('./path/to/model');
User.updateUser(user.name, function(err, user) {
if(err) {
var error = new Error('Already exists!');
error.status = 401;
return next(error);
}
});
既に投稿された例に加えて、express-async-wrapと非同期関数(ES2017)を使用した別のアプローチがあります。
ルーター
router.put('/:id/settings/profile', wrap(async function (request, response, next) {
const username = request.body.username
const email = request.body.email
const userWithEmail = await userService.findUserByEmail(email)
if (userWithEmail) {
return response.status(409).send({message: 'Email is already taken.'})
}
const userWithUsername = await userService.findUserByUsername(username)
if (userWithUsername) {
return response.status(409).send({message: 'Username is already taken.'})
}
const user = await userService.updateProfileSettings(userId, username, email)
return response.status(200).json({user: user})
}))
UserService
async function updateProfileSettings (userId, username, email) {
try {
return User.findOneAndUpdate({'_id': userId}, {
$set: {
'username': username,
'auth.email': email
}
}, {new: true})
} catch (error) {
throw new Error(`Unable to update user with id "${userId}".`)
}
}
async function findUserByEmail (email) {
try {
return User.findOne({'auth.email': email.toLowerCase()})
} catch (error) {
throw new Error(`Unable to connect to the database.`)
}
}
async function findUserByUsername (username) {
try {
return User.findOne({'username': username})
} catch (error) {
throw new Error(`Unable to connect to the database.`)
}
}
// other methods
export default {
updateProfileSettings,
findUserByEmail,
findUserByUsername,
}
リソース
一意のインデックスで検索する場合、UserModel.countを使用する方が、intだけを返すのではなく、ドキュメント全体を返す(読み取りを行う)ため、実際にはUserModel.findOneよりも優れている場合があります。
この古いソリューションに陥る人のために。より良い方法があります mongooseドキュメントから 。
var s = new Schema({ name: { type: String, unique: true }});
s.path('name').index({ unique: true });