处理Mongoose验证错误 - 在哪里以及如何?

47 validation mongoose mongodb node.js

我正在尝试决定如何处理Mongoose中的验证错误.

使用节点验证程序的自定义错误消息

我使用node-validator定义了自己的验证规则,例如:

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

这会产生如下错误:

  username: 
   { message: 'Validator "Username cannot be blank" failed for path username',
     name: 'ValidatorError',
     path: 'username',
     type: 'Username cannot be blank' },
Run Code Online (Sandbox Code Playgroud)

使用mongoose-validator

但是,node-validator提供自己的错误消息.如果我使用mongoose-validator节点模块将node-validator直接插入我的模式,那么我可以直接使用这些错误消息:

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

这将生成如下所示的错误消息:

  name: 
   { message: 'Validator "String is empty" failed for path name',
     name: 'ValidatorError',
     path: 'name',
     type: 'String is empty' } }
Run Code Online (Sandbox Code Playgroud)

我也可以在这里提供自定义错误消息:

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

猫鼬required

Mongoose允许您根据需要定义字段:

var UserSchema = new Schema({
  name: { type: String, required: true }
});
Run Code Online (Sandbox Code Playgroud)

这将生成如下所示的错误消息:

  name: 
   { message: 'Validator "required" failed for path name',
     name: 'ValidatorError',
     path: 'name',
     type: 'required' } }
Run Code Online (Sandbox Code Playgroud)

这个问题

感觉好像这些验证器要求您使用它们的内置错误消息.例如,我想声明一个required如上所示的字段,但我找不到自定义错误消息的方法.而mongoose-validator模块直到最近才支持自定义消息,这让我觉得它们是模型级别的反模式.

实现这些验证器的最佳方法是什么?我应该让他们产生自己的错误,然后以某种方式解释它们吗?

tha*_*las 33

在这一点上,购买mongoose如何处理错误似乎是合乎逻辑的.

您不希望模型处理错误消息.表示层(控制器?)应该依赖于type决定哪个是最好的用户友好消息(i18n考虑).

还存在使用中间件进行验证的情况.在这种情况下,将显示给控制器的错误消息是您传递给next()回调的任何内容.

因此,对于中间件的情况,虽然没有记录,但为了在模型中保持一致的验证API,您应该直接使用Mongoose的Error构造函数:

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();
});
Run Code Online (Sandbox Code Playgroud)

这样,即使验证错误源自中间件,也可以保证一致的验证错误处理.

为了将错误消息与类型正确匹配,我将创建一个枚举,它将充当所有可能类型的静态映射:

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

    }
  });
});
Run Code Online (Sandbox Code Playgroud)

  • 不,不要这样......消息应该在他们被发现的地方产生...... (13认同)

twk*_*nab 9

我知道验证器插件可能有用,但我认为mongoose验证的东西比它真的很复杂更令人生畏.从外面来看它看起来确实很复杂但是一旦你开始撕裂它,它就不那么糟糕了.

如果您查看下面的代码,您将看到一个示例,说明如何使用内置验证器返回自定义错误消息.

在设置字段时,您所要做的就是设置第二个参数,并带有您自己的自定义错误消息.

检查下面的requiredminlengthmaxlength字段以查看我如何设置自定义错误消息,然后查看以下方法,了解如何访问错误对象或将错误对象发送到前端:

// 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);
Run Code Online (Sandbox Code Playgroud)

以上设置我们的字段以具有自定义错误消息.但是我们如何访问它们或将它们发送到我们的前端?我们可以在服务器控制器中设置以下方法,其响应数据将发送回角度:

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);
                }
            })
    },
};
Run Code Online (Sandbox Code Playgroud)

如果你运行上面的代码,并且我们的任何mongoose验证器都没有通过,那么error(err)对象将被.catch()promise中的对象抓取.如果您控制台记录此错误,您将在该对象中看到我们的自定义消息,具体取决于标记的错误.

注:以上例子只是为了添加自定义验证消息给猫鼬拥有已经内置验证(如required,minlength,maxlength等等).

如果要创建更高级的验证,例如针对正则表达式模式验证字段等,则必须创建自定义validator函数.

请参阅此链接中的"自定义验证程序"部分,以获取有关如何在您的字段中添加验证程序的一个很好的示例:http://mongoosejs.com/docs/validation.html.

注意:您也可以使用"预保存挂钩"和"实例方法",但这超出了本问题的范围,内置验证器和"自定义验证器"(上述链接)是更容易的路径.

希望这可以帮助!

  • 好的,我在这些方面添加了一些内容:) (2认同)
  • +UpTheCreek 我喜欢这些变化,以及 422 状态的使用!聪明的!+1 祝你一切顺利!:D (2认同)

小智 3

您需要问自己的问题是,谁首先应该对导致错误负责?

如果这种情况发生在您可以控制的系统中,只需像平常一样让错误击中您,并在进行过程中清除错误,但我怀疑您正在开发一个面向现实世界用户的应用程序,并且你想净化他们的输入。

我建议客户端在将输入发送到服务器之前检查输入是否正确,并显示良好的帮助消息,例如“您的用户名必须位于 x 和 y 字符之间”。

然后在服务器端,您期望在 99% 的情况下输入将直接来自您的清理客户端,因此您仍然使用您已经建议的技术来验证它,但如果出现错误,您只需返回一般错误消息用户界面 - 因为您相信您的用户界面会显示帮助程序消息,所以验证错误一定是由错误或黑客尝试引起的。

请记住记录所有服务器端验证错误,因为它们可能是严重的错误或有人在寻找漏洞。