Manejo de los errores de validación de la mongoose: ¿dónde y cómo?

Estoy tratando de decidir cómo quiero manejar los errores de validación en Mongoose.

Mensajes de error personalizados usando el validador de nodos

He definido mis propias reglas de validación usando el validador de nodos , por ejemplo:

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

Que generará un error que se ve así:

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

Usando el validador de mongoose

Sin embargo, el validador de nodos proporciona sus propios mensajes de error. Si uso el módulo Nodo del validador de mongoose para conectar el validador de nodos directamente en mi esquema, entonces puedo usar estos mensajes de error directamente:

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

Que generará un mensaje de error que se parece a:

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

También puedo proporcionar un mensaje de error personalizado aquí también:

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

Mongoose required bandera

Mongoose le permite definir un campo como se requiere:

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

Que generará un mensaje de error que se parece a:

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

La pregunta

Se siente como si estos validadores quisieran que usaran sus mensajes de error incorporados. Por ejemplo, quiero declarar un campo como se required como se vio anteriormente, pero no puedo encontrar una manera de personalizar el mensaje de error. Y el módulo validador de mongoose no admitía mensajes personalizados hasta hace muy poco, lo que me hace pensar que son un antipatrón en el nivel del modelo.

¿Cuál es la mejor manera de implementar estos validadores? ¿Debo dejar que generen sus propios errores y luego interpretarlos de alguna manera?

En este punto, parece lógico darse cuenta de cómo Mangosta maneja los errores.

No querría que sus modelos manejen mensajes de error. La capa de presentación (¿controladores?) Debe basarse en el type para decidir cuál es el mejor mensaje fácil de usar para mostrar (se considera i18n).

También existe el caso en el que la validación puede realizarse utilizando un middleware . En este caso, el mensaje de error que aparecerá en su controlador es lo que pase a la next() callback.

Por lo tanto, para el caso de middleware, aunque no esté documentado, para mantener una API de validación consistente en sus modelos, debe usar directamente los constructores de error 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 esa manera, se le garantiza un manejo de error de validación consistente incluso si el error de validación se origina en un middleware.

Para hacer coincidir adecuadamente los mensajes de error con los tipos, crearía una enumeración que actuaría como un mapa estático para todos los tipos posibles:

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

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

Mensajes de error Los mensajes de error personalizados ahora están nuevamente en 0.2.1 y se pueden configurar a través del objeto de opciones:

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


Cómo implementé esto:

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

Mi parte delantera se ocupa de lo requerido, por lo que nunca espero que el error “requerido” de la mongoose llegue al usuario, sino más bien una protección segura de la parte posterior.

Sé que los complementos de validación son probablemente útiles, pero creo que la validación de la mongoose es más intimidante de lo que realmente es complicado. Definitivamente parece complicado desde el exterior, pero una vez que empiezas a desgarrarlo, no es tan malo.

Si revisa el código a continuación, verá un ejemplo de cómo puede devolverse un mensaje de error personalizado utilizando validadores incorporados.

Todo lo que tiene que hacer es configurar un segundo parámetro, con su propio mensaje de error personalizado, al configurar sus campos.

maxlength campos required minlength y maxlength continuación para ver cómo he configurado un mensaje de error personalizado, y luego verifique los métodos a continuación sobre cómo se puede acceder al objeto de error o enviarlo al frente:

 // 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, // username must be unique dropDups: true, }, // end username field }, { timestamps: true, }, ); // Export the schema: module.exports = mongoose.model('User', UserSchema); 

Lo anterior configura nuestros campos para tener mensajes de error personalizados. Pero, ¿cómo podemos acceder a ellos o enviarlos a nuestro extremo delantero? Podríamos tener el siguiente método de configuración en nuestro controlador de servidor, cuyos datos de respuesta se envían de nuevo a 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 ejecutó el código anterior y cualquiera de nuestros validadores de mongoose no pasó, el objeto de error ( err ) será capturado por el .catch() en la promesa. Si la consola registra este error, verá que ese objeto es nuestro mensaje personalizado, según el error que se haya marcado.

Nota: el ejemplo anterior es solo para agregar mensajes de validación personalizados a las validaciones ya integradas que posee Mongoose (como se required , minlength , maxlength , etc.).

Si desea crear validaciones más avanzadas, como la validación de campos contra patrones de expresiones regulares o similares, entonces tendrá que crear funciones de validator personalizadas.

Consulte la sección “Validadores personalizados” en este enlace para ver un gran ejemplo de cómo agregar un validador a su campo: http://mongoosejs.com/docs/validation.html .

Nota: También puede usar “ganchos de guardado previo” y “métodos de instancia”, pero esto está más allá del scope de esta pregunta y los validadores integrados y los “Validadores personalizados” (enlaces mencionados anteriormente) son rutas más fáciles.

¡Espero que esto ayude!

La pregunta que debe hacerse es quién es el responsable de causar el error en primer lugar.

Si esto sucede en su sistema, sobre el cual tiene el control, simplemente deje que los errores lo golpeen como lo haría normalmente, y elimine los errores a medida que avanza, pero sospecho que está haciendo una aplicación que se enfrenta a usuarios del mundo real y Quieres sanear sus entradas.

Le recomendaría al lado del cliente que verifique que la entrada sea correcta antes de enviarla a su servidor, y que muestre buenos mensajes de ayuda como “Su nombre de usuario debe estar entre los caracteres x e y”.

Luego, en el lado del servidor, espera que en el 99% de los casos la entrada provenga directamente de su cliente de desinfección, por lo que aún la valide utilizando las técnicas que ya sugirió, pero si hay un error, simplemente devuelve un mensaje de error general a la interfaz de usuario, ya que confía en que su interfaz de usuario habría mostrado mensajes de ayuda, por lo que el error de validación debe ser causado por un error o un bash de pirateo.

Recuerde registrar todos los errores de validación del lado del servidor, ya que podrían ser errores graves o alguien que busque vulnerabilidades.

Advertencia: a partir de Mongoose 4.1.3, la firma para la función ValidatorError ha cambiado completamente y la siguiente información ya no es aplicable:

A partir de Mongoose 3.8.12, la firma para la función ValidatorError es:

 function ValidatorError (path, msg, type, val) 

Donde tipo puede ser “no válido” o “requerido”

Por ejemplo, si la validación de su campo de “correo electrónico” genera un error de validación, simplemente puede hacer:

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

A partir de Mongoose 4.5.0 Document # invalidate devuelve un ValidationError. Consulte este https://github.com/Automattic/mongoose/issues/3964

Además, cuando intenta invalidar un enlace de búsqueda findOneAndUpdate, puede hacer:

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