web-dev-qa-db-fra.com

Gérer les erreurs de validation de Mongoose - où et comment?

J'essaie de décider comment je veux gérer les erreurs de validation dans Mongoose.

Messages d'erreur personnalisés à l'aide du validateur de noeud

J'ai défini mes propres règles de validation en utilisant node-validator , par exemple:

UserSchema.path('username')
  .validate(function (username) {
    return validator.check(username).notEmpty()
  }, 'Username cannot be blank')

Ce qui va générer une erreur qui ressemble à ceci:

  username: 
   { message: 'Validator "Username cannot be blank" failed for path username',
     name: 'ValidatorError',
     path: 'username',
     type: 'Username cannot be blank' },

Utilisation du validateur mangouste

Cependant, le validateur de noeud fournit ses propres messages d'erreur. Si j'utilise le module mongoose-validator Node pour brancher node-validator directement dans mon schéma, je peux utiliser ces messages d'erreur directement à la place:

var UserSchema = new Schema({
  name: { type: String, validate: [validate('notEmpty')] }
});

Ce qui va générer un message d'erreur qui ressemble à:

  name: 
   { message: 'Validator "String is empty" failed for path name',
     name: 'ValidatorError',
     path: 'name',
     type: 'String is empty' } }

Je peux également fournir ici un message d'erreur personnalisé:

var UserSchema = new Schema({
  name: { type: String, validate: [validate({message: 'Name cannot be blank' }, 'notEmpty')] }
});

Mangouste required flag

Mongoose vous permet de définir un champ comme requis:

var UserSchema = new Schema({
  name: { type: String, required: true }
});

Ce qui va générer un message d'erreur qui ressemble à:

  name: 
   { message: 'Validator "required" failed for path name',
     name: 'ValidatorError',
     path: 'name',
     type: 'required' } }

La question

Il semble que ces validateurs souhaitent que vous utilisiez leurs messages d'erreur intégrés. Par exemple, je veux déclarer un champ comme required comme vu ci-dessus, mais je ne trouve pas de moyen de personnaliser le message d'erreur. Et le module de validation de mangouste n'a pas pris en charge les messages personnalisés jusqu'à très récemment, ce qui me fait penser qu'ils sont un anti-modèle au niveau du modèle.

Quelle est la meilleure façon de mettre en œuvre ces validateurs? Dois-je les laisser générer leurs propres erreurs et ensuite les interpréter d'une manière ou d'une autre?

44
user1082754

À ce stade, il semble logique de comprendre comment la mangouste gère les erreurs.

Vous ne voudriez pas que vos modèles gèrent les messages d'erreur. La couche de présentation (contrôleurs?) Doit s'appuyer sur type pour décider quel est le meilleur message convivial à afficher (i18n considéré).

Il y a aussi le cas où la validation peut se faire par en utilisant un middleware . Dans ce cas, le message d'erreur qui apparaîtra sur votre contrôleur est tout ce que vous transmettez au rappel next().

Ainsi, pour le cas des middlewares, bien que non documentés, afin de conserver une API de validation cohérente sur tous vos modèles, vous devez utiliser directement les constructeurs d'erreur de Mongoose:

var mongoose = require('mongoose');
var ValidationError = mongoose.Error.ValidationError;
var ValidatorError  = mongoose.Error.ValidatorError;

schema.pre('save', function (next) {
  if (/someregex/i.test(this.email)) {
    var error = new ValidationError(this);
    error.errors.email = new ValidatorError('email', 'Email is not valid', 'notvalid', this.email);
    return next(error);
  }

  next();
});

De cette façon, vous êtes assuré d'une gestion cohérente des erreurs de validation même si l'erreur de validation provient d'un middleware.

Pour faire correspondre correctement les messages d'erreur aux types, je créerais une énumération qui agirait comme une carte statique pour tous les types possibles:

// my controller.js

var ValidationErrors = {
  REQUIRED: 'required',
  NOTVALID: 'notvalid',
  /* ... */
};


app.post('/register', function(req, res){
  var user = new userModel.Model(req.body);

  user.save(function(err){
    if (err) {
      var errMessage = '';

      // go through all the errors...
      for (var errName in err.errors) {
        switch(err.errors[errName].type) {
          case ValidationErrors.REQUIRED:
            errMessage = i18n('Field is required');
            break;
          case ValidationErrors.NOTVALID:
            errMessage = i18n('Field is not valid');
            break;
        }
      }
      res.send(errMessage);

    }
  });
});
30
thanpolas

Je sais que les plugins de validation sont probablement utiles, mais je pense que la validation de mangouste est plus intimidante que compliquée. Cela semble définitivement compliqué de l'extérieur, mais une fois que vous commencez à vous déchirer, ce n'est pas si mal.

Si vous extrayez le code ci-dessous, vous verrez un exemple de la façon dont un message d'erreur personnalisé peut être renvoyé à l'aide de validateurs intégrés.

Tout ce que vous avez à faire est de définir un deuxième paramètre, avec votre propre message d'erreur personnalisé, lors de la configuration de vos champs.

Consultez les champs required et minlength et maxlength ci-dessous pour voir comment j'ai configuré un message d'erreur personnalisé, puis consultez les méthodes ci-dessous pour savoir comment l'objet d'erreur peut être accessible ou envoyé à la partie frontale:

// Grab dependencies:
var mongoose = require('mongoose');

// Setup a schema:
var UserSchema = new mongoose.Schema (
    {
        username: {
            type: String,
            minlength: [2, 'Username must be at least 2 characters.'],
            maxlength: [20, 'Username must be less than 20 characters.'],
            required: [true, 'Your username cannot be blank.'],
            trim: true,
            unique: true,
            dropDups: true,
        }, // end username field
    },
    {
        timestamps: true,
    },
);

// Export the schema:
module.exports = mongoose.model('User', UserSchema);

Ce qui précède configure nos champs pour avoir des messages d'erreur personnalisés. Mais comment pouvons-nous y accéder ou les envoyer à notre front-end? Nous pourrions avoir la configuration de méthode suivante dans notre contrôleur de serveur, dont les données de réponse sont renvoyées à angular:

var myControllerMethods = {
    register : function(req, res) {
        // Create a user based on the schema we created:
        User.create(req.body)
            .then(function(newUser) {
                console.log('New User Created!', newUser);
                res.json(newUser);
            })
            .catch(function(err) {
                if (err.name == 'ValidationError') {
                    console.error('Error Validating!', err);
                    res.status(422).json(err);
                } else {
                    console.error(err);
                    res.status(500).json(err);
                }
            })
    },
};

Si vous avez exécuté le code ci-dessus et que l'un de nos validateurs mangouste n'a pas réussi, l'objet erreur (err) sera saisi par la .catch() dans la promesse. Si vous console consignez cette erreur, vous verrez dans cet objet est notre message personnalisé, en fonction de l'erreur signalée.

Remarque: l'exemple ci-dessus est juste pour ajouter des messages de validation personnalisés aux validations déjà intégrées que Mongoose possède (comme required, minlength, maxlength et ainsi de suite).

Si vous souhaitez créer des validations plus avancées, telles que la validation de champs par rapport à des modèles d'expression régulière ou similaires, vous devrez créer des fonctions validator personnalisées.

Voir la section "Validateurs personnalisés" sur ce lien pour un excellent exemple de comment ajouter un validateur directement dans votre champ: http://mongoosejs.com/docs/validation.html .

Remarque: Vous pouvez également utiliser des "hooks de pré-sauvegarde" et des "méthodes d'instance", mais cela dépasse le cadre de cette question et les validateurs intégrés et les "validateurs personnalisés" (lien ci-dessus) sont des itinéraires plus faciles.

J'espère que cela t'aides!

8
twknab

De Mongoose: https://github.com/leepowellcouk/mongoose-validator

Messages d'erreur Les messages d'erreur personnalisés sont maintenant de retour en 0.2.1 et peuvent être définis via l'objet options:

validate({message: "String should be between 3 and 50 characters"}, 'len', 3, 50)


Comment j'ai implémenté cela:

var emailValidator = [validate({message: "Email Address should be between 5 and 64 characters"},'len', 5, 64), validate({message: "Email Address is not correct"},'isEmail')];

var XXXX = new Schema({
email : {type: String, required: true, validate: emailValidator} }); 

Mon front-end traite de requis, donc je ne m'attends jamais à ce que l'erreur de mangouste "requis" se produise pour l'utilisateur, plus d'un garde-bus arrière.

4
Matt Ritz

La question que vous devez vous poser est qui est responsable de l'erreur en premier lieu?

Si cela se produit dans votre système, que vous contrôlez, laissez les erreurs vous frapper comme vous le feriez normalement et éliminez les bogues au fur et à mesure, mais je soupçonne que vous faites une application qui fait face à des utilisateurs du monde réel et vous souhaitez assainir leurs entrées.

Je recommanderais au client de vérifier que l'entrée est correcte avant de l'envoyer à votre serveur, et de montrer des messages d'aide sympas comme "Votre nom d'utilisateur doit être compris entre x et y caractères".

Ensuite, côté serveur, vous vous attendez à ce que dans 99% des cas, l'entrée provienne directement de votre client de désinfection, et donc vous la validez toujours en utilisant les techniques que vous avez déjà suggérées, mais s'il y a une erreur, vous renvoyez simplement un message d'erreur général à l'interface utilisateur - puisque vous faites confiance à ce que votre interface utilisateur aurait affiché des messages d'assistance, donc l'erreur de validation doit être causée par un bogue ou une tentative de piratage.

N'oubliez pas de consigner toutes les erreurs de validation côté serveur car il peut s'agir de graves bogues ou de la recherche d'exploits.

3
ExxKA

Avertissement: Depuis Mongoose 4.1.3, la signature de la fonction ValidatorError a complètement changé et les informations ci-dessous ne sont plus applicables:

Depuis Mongoose 3.8.12, la signature de la fonction ValidatorError est:

function ValidatorError (path, msg, type, val) 

Où le type peut être "non valide" ou "requis"

Par exemple, si votre validation de champ "email" soulève une erreur de validation, vous pouvez simplement faire:

var error = new ValidationError(this);
error.errors.email = 
      new ValidatorError('email', "Your err message.", 'notvalid', this.email);
2
salihcenap

Voir le package hmv , qui vous aide à personnaliser les modèles de message d'erreur mangouste, y compris les erreurs d'index uniques:

template : {PATH_NAME} must be at least {MIN_LENGTH} characters long
schema   : { fullname : { type : String, min : 3, $name : 'Full name' } }
message  : Full name must be at least 3 characters long

template : {PATH_NAME} {VALUE} have been used, please choose another
schema   : { username : { type : String, unique : true } }
message  : username MrBean have been used, please choose another

Et prend spécifiquement en charge la localisation, ex en vietnamien:

template : {PATH_NAME} dài ít nhất {MIN_LENGTH} kí tự
schema   : { fullname : { type : String, min : 3, $name : 'tên tài khoản' } }
message  : Tên tài khoản dài ít nhất 5 kí tự

Le bon point est que vous n'avez besoin de personnaliser le modèle de message qu'une seule fois, au lieu de personnaliser chaque champ de chaque schéma dans l'approche précédente.

1
Dinh Hoang

Depuis la mangouste 4.5.0, le document # invalidate renvoie une ValidationError. Voir ceci https://github.com/Automattic/mongoose/issues/3964

En outre, lorsque vous essayez d'invalider un hook de requête findOneAndUpdate, vous pouvez effectuer:

// pass null because there is no document instance
let err = new ValidationError(null)
err.errors[path] = new ValidatorError({
    path: 'postalCode',
    message: 'postalCode not supported on zones',
    type: 'notvalid',
    value,
})
throw(err)
1
ztrange