Mongoose中验证的一些基本例子

568 阅读6分钟

就像所有其他框架一样,Mongoose提供了一种方法,在你将数据保存到数据库之前验证数据。数据验证对于确保 "坏 "数据不会被持久化在你的应用程序中非常重要。在向MongoDB插入数据时,使用Mongoose的一个好处是它对数据模式的内置支持,以及在数据被持久化时对其进行自动验证。如果没有Mongoose,你就不会得到这些。Mongoose的验证器很容易配置。在定义模式的时候,开发者可以为应该被验证的属性添加额外的选项。现在让我们看一下Mongoose中验证的一些基本例子。


开始使用required

现在我们在Mongoose中有一个没有验证的模式。换句话说,下面定义的所有属性都是可选的。如果你在创建文档时提供了每个属性,很好!如果没有,也很好。如果没有,那也很好!

const gameSchema = new mongoose.Schema({
    title: String,
    publisher: String,
    tags: [String],
    date: { type: Date, default: Date.now },
    onSale: Boolean,
    price: Number
});

当然,几乎总是需要验证任何你想持久保存的数据。我们可以修改模式,像这样添加验证。在下面的片段中,我们将游戏的title ,这是强制性的。它不再是可选的了。我们可以通过required 属性来实现这一点。

const gameSchema = new mongoose.Schema({
    title: { type: String, required: true },
    publisher: String,
    tags: [String],
    date: { type: Date, default: Date.now },
    onSale: Boolean,
    price: Number
});

随着我们对游戏标题的验证到位,让我们尝试在不指定标题的情况下将游戏保存到数据库。

const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost/mongo-games')
    .then(() => console.log('Now connected to MongoDB!'))
    .catch(err => console.error('Something went wrong', err));

const gameSchema = new mongoose.Schema({
    title: { type: String, required: true },
    publisher: String,
    tags: [String],
    date: { type: Date, default: Date.now },
    onSale: Boolean,
    price: Number
});

const Game = mongoose.model('Game', gameSchema);

async function saveGame() {
    const game = new Game({
        publisher: "Nintendo",
        tags: ["adventure", "action"],
        onSale: false,
        price: 59.99,
    });

    const result = await game.save();
    console.log(result);
}

saveGame();

我们可以在终端使用node index.js ,运行index.js来测试这个。

mongo-crud $node index.js
(node:10176) UnhandledPromiseRejectionWarning: Unhandled promise rejection 
(rejection id: 2): ValidationError: Game validation failed: title: Path `title` 
is required. (node:10176) [DEP0018] DeprecationWarning: Unhandled promise rejections 
are deprecated. In the future, promise rejections that are not handled will 
terminate the Node.js process with a non-zero exit code.

有趣的是。我们得到了很多关于未处理的拒绝承诺的错误信息。我们可以通过更新我们在saveGame() 函数中的逻辑来解决这个问题。然而,好的方面是,在列出的信息中,我们确实看到验证工作,因为游戏验证失败:标题。路径`title`是必须的,这告诉我们。让我们更新代码,通过实现一个try/catch块来正确处理承诺。

async function saveGame() {
    const game = new Game({
        publisher: "Nintendo",
        tags: ["adventure", "action"],
        onSale: false,
        price: 59.99,
    });

    try {
        const result = await game.save();
        console.log(result);
    } catch (err) {
        console.log(err.message)
    }
}

saveGame();

运行index.js文件,现在给我们一个更容易阅读的信息。

mongo-crud $node index.js
Game validation failed: title: Path `title` is required.

很好!验证开始工作了。需要注意的是,这个在Mongoose中验证的例子仅仅是在Mongoose中验证。这与数据库或MongoDb级别的数据验证没有关系。另一个需要注意的是,Mongoose中的这种验证是对像Joi这样的验证包的补充,我们在node rest api教程中使用了Joi。通过在REST层和Mongoose层同时使用验证,你可以确保有问题的文档不会被持久化到数据库中。


关于内置验证器的更多信息

在上面的章节中,我们看到了如何使用required 属性来使用户在持久化到数据库时必须提供游戏的标题。你也可以使用一个带有required 的函数来有条件地要求某些东西。在下面的片段中,我们说如果游戏正在销售,那么价格是必须的。

const gameSchema = new mongoose.Schema({
    title: { type: String, required: true },
    publisher: String,
    tags: [String],
    date: { type: Date, default: Date.now },
    onSale: Boolean,
    price: {
        type: Number,
        required: function () { return this.onSale }
    }
});

现在我们可以尝试保存一个游戏,但我们将省略标题和价格,看看我们的验证规则是否仍然有效。我们插入游戏的逻辑在这里。

const Game = mongoose.model('Game', gameSchema);

async function saveGame() {
    const game = new Game({
        publisher: "Nintendo",
        tags: ["adventure", "action"],
        onSale: true,
    });

    try {
        const result = await game.save();
        console.log(result);
    } catch (err) {
        console.log(err.message)
    }
}

saveGame();

运行程序显示,这些规则运行良好。价格和标题都是必需的,所以游戏不会被持久化。

mongo-crud $node index.js
Game validation failed: price: Path `price` is required., title: Path `title` is required.

minlength和maxlength

除了使一个字符串成为必需品外,你还可以指定它的最小长度和最大长度。考虑一下这个模式。

const gameSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true,
        minlength: 4,
        maxlength: 200
    },
    publisher: String,
    tags: [String],
    date: { type: Date, default: Date.now },
    onSale: Boolean,
    price: {
        type: Number,
        required: function () { return this.onSale }
    }
});

现在,让我们提供一个标题,但只有3个字符,看看会发生什么。

const Game = mongoose.model('Game', gameSchema);

async function saveGame() {
    const game = new Game({
        title: "Pac",
        publisher: "Nintendo",
        tags: ["adventure", "action"],
        onSale: true,
    });

    try {
        const result = await game.save();
        console.log(result);
    } catch (err) {
        console.log(err.message)
    }
}

saveGame();

当我们运行这个程序时,验证器告诉我们,我们的标题太短了。

mongo-crud $node index.js
Game validation failed: price: Path `price` is required., 
title: Path `title` (`Pac`) is shorter than the minimum allowed length (4).

枚举验证

当创建一个游戏时,我们要给它分配一些标签。使用枚举验证,我们可以指定一个人可以使用的标签。下面我们说,一个游戏的标签必须是体育、赛车、动作或Rpg中的任何一个。

const gameSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true,
        minlength: 4,
        maxlength: 200
    },
    publisher: String,
    tags: {
        type: [String],
        required: true,
        enum: ['sports', 'racing', 'action', 'rpg']
    },
    date: { type: Date, default: Date.now },
    onSale: Boolean,
    price: {
        type: Number,
        required: function () { return this.onSale }
    }
});

现在我们尝试用一个我们在枚举验证中没有考虑的标签--冒险--来保存一个游戏。

const Game = mongoose.model('Game', gameSchema);

async function saveGame() {
    const game = new Game({
        title: "Pacman",
        publisher: "Nintendo",
        tags: ["adventure", "action"],
        onSale: true,
        price: 29.99
    });

    try {
        const result = await game.save();
        console.log(result);
    } catch (err) {
        console.log(err.message)
    }
}

saveGame();

果然,试图将该游戏插入数据库时失败了,我们得到的错误是 "adventure "不是路径 "tags "的有效枚举值。

mongo-crud $node index.js Game validation failed: tags.0: `adventure` is not a valid enum value for path `tags`.

自定义验证器

你也可以在Mongoose中设置一个自定义验证器。这里我们将修改标签的验证,使用户必须提供一个以上的标签。

const gameSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true,
        minlength: 4,
        maxlength: 200
    },
    publisher: String,
    tags: {
        type: [String],
        validate: {
            validator: function (v) {
                return v.length > 1
            },
            message: 'You must provide more than 1 tag.'
        }
    },
    date: { type: Date, default: Date.now },
    onSale: Boolean,
    price: {
        type: Number,
        required: function () { return this.onSale }
    }
});

现在我们尝试保存一个游戏,但只提供一个标签。

const Game = mongoose.model('Game', gameSchema);

async function saveGame() {
    const game = new Game({
        title: "Pacman",
        publisher: "Nintendo",
        tags: ["arcade"],
        onSale: true,
        price: 29.99
    });

    try {
        const result = await game.save();
        console.log(result);
    } catch (err) {
        console.log(err.message)
    }
}

saveGame();

运行该程序后,我们会得到我们所期望的验证错误。

mongo-crud $node index.js
Game validation failed: tags: You must provide more than 1 tag.

异步验证器

当你需要获取一些远程数据,或在持久化到数据库之前执行一些其他类型的异步任务时,异步验证就会发挥作用。为此,我们可以使用一个异步验证器。让我们来看看一个。我们将用setTimeout()函数来模拟异步工作。

const gameSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true,
        minlength: 4,
        maxlength: 200
    },
    publisher: String,
    tags: {
        type: [String],
        validate: {
            isAsync: true,
            validator: function (v, callback) {
                // Complete async task
                setTimeout(() => {
                    const result = v.length > 1;
                    callback(result);
                }, 2000);
            },
            message: 'You must provide more than 1 tag.'
        }
    },
    date: { type: Date, default: Date.now },
    onSale: Boolean,
    price: {
        type: Number,
        required: function () { return this.onSale }
    }
});

要启用异步验证,你需要做的就是给validate对象添加isAsync属性,并将其设置为true 。然后你就可以进行异步工作了,无论是获取远程数据、从文件系统读取数据,还是与数据库一起工作,验证都会正常进行。


Mongoose验证实例总结

在这个关于Mongoose验证的教程中,我们了解到在定义模式时,你可以将一个属性的类型设置为SchemaType对象。你用这个对象来定义给定属性的验证要求。我们可以用这样的代码添加验证。

new mongoose.Schema({
    name: { type: String, required: true }
})

在文档可以被保存到数据库之前,验证逻辑由Mongoose执行。也可以通过调用validate()方法手动触发它。一些内置的验证器包括。

  • 字符串: minlength,maxlength,matchenum
  • 数字: min,max
  • 日期: min,max
  • 所有类型: required

为了设置自定义验证,你可以设置验证对象并在验证属性中使用一个函数。

tags: [
    type: Array,
    validate: {
        validator: function (v) { return v && v.length > 0; },
        message: 'A game should have at least 1 tag.'
    }
]

当与数据库或远程服务对话以执行验证时,需要使用一个异步验证器。你可以通过将isAsync 属性设置为true 来启用这一点。

validate: {
    isAsync: true
    validator: function(v, callback) {
        // Do the validation, when the result is ready, call the callback
        callback(isValid);
    }
}

其他一些有用的SchemaType属性包括。

  • 字符串: lowercase,uppercasetrim
  • 所有类型: get,set (定义一个自定义的getter/setter)

快乐的验证!