2013-06-02 4 views
14

Wie werden die Validierungen durchgeführt, bevor die bearbeiteten Daten in Mungo gespeichert werden?Wie überprüft man, ob diese Daten während der Aktualisierung bereits in der Datenbank vorhanden sind (Mongoose And Express)

Zum Beispiel, wenn sample.name bereits in der Datenbank vorhanden ist, kann der Benutzer eine gewisse Art von Fehler empfangen wird, so ähnlich, hier ist mein Code unten

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

    }); 
}); 

Antwort

36

Typischerweise Sie mongoose validation verwenden könnten, aber da Sie benötigen eine Async-Ergebnis (DB-Abfrage für vorhandene Namen) und Validatoren unterstützen keine Versprechen (von dem, was ich sagen kann), müssen Sie Ihre eigene Funktion erstellen und einen Rückruf übergeben. Hier ein Beispiel:

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

     }); 
    } 
}); 

UPDATE: Eine bessere Art

Die Pre Haken ein natürlicher Ort zu sein scheint, die sparen zu stoppen:

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

UPDATE 2: Async benutzerdefinierte Validatoren

Es sieht aus wie Mungo unterstützt asynchrone benutzerdefinierte Validatoren jetzt s o das wäre wahrscheinlich die natürliche Lösung sein:

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

Sie sind nicht „next()“, wenn der Benutzer aufrufen vorhanden ist, denke ich, sollten Sie() mit einem Fehler –

+3

nächsten nennen, warum nicht nur die 'unique' param auf die Verwendung von Namensfeld? – udidu

+0

Wie wird der Benutzer die save() Methode haben? –

4

Eine andere Art und Weise mit dem Beispiel @nfreeze verwendet, um fortzufahren ist diese Validierungsmethode:

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'); 
+1

Was ist Pfad? Könnten Sie dies näher ausführen? –

+0

Das funktionierte für mich und scheint der eleganteste Weg zu sein, aber es wirft einen Depretionierungsfehler auf, dh DeprecationWarning: Implizite asynchrone benutzerdefinierte Validatoren (benutzerdefinierte Validatoren, die 2 Argumente annehmen) sind in mongoose> = 4.9.0 veraltet . – Marko

+0

http://mongoosejs.com/docs/validation.html#async-custom-validators Auch wenn Sie keine asynchronen Validatoren verwenden möchten, seien Sie vorsichtig, denn Mungo 4 wird davon ausgehen, dass alle Funktionen zwei Argumente haben sind asynchron, wie die validator.isEmail-Funktion. Dieses Verhalten gilt ab Version 4.9.0 als veraltet, und Sie können es ausschalten, indem Sie isAsync: false für Ihren benutzerdefinierten Validator angeben. – Marko

0

Wenn Sie durch einen eindeutigen Index sind, dann mit UserModel.count kann tatsächlich besser für Sie sein als UserModel.findOne, weil es das gesamte Dokument zurückgibt (dh einen Lesevorgang ausführt), anstatt nur einen int zurückzugeben.

2

Hier ist eine andere Möglichkeit, dies in weniger Code zu erreichen.

UPDATE 3: Asynchronous Modellklasse Statik

ähnlich Option 2, dies ermöglicht es Ihnen, eine Funktion direkt auf das Schema verknüpft zu schaffen, sondern aus der gleichen Datei mit Hilfe des Modell genannt.

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

Aufruf von Datei

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

Neben Beispielen bereits gebucht, hier ist ein weiterer Ansatz Express-Asynchron-wrap und asynchrone Funktionen (ES2017).

Router

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, 
} 

Ressourcen

async function

await

express-async-wrap

Verwandte Themen