验证 & 约束
在本教程中,你将学习如何在 Sequelize 中设置模型的验证和约束.
对于本教程,将假定以下设置:
const { Sequelize, Op, Model, DataTypes } = require("sequelize");
const sequelize = new Sequelize("sqlite::memory:");
const User = sequelize.define("user", {
username: {
type: DataTypes.TEXT,
allowNull: false,
unique: true
},
hashedPassword: {
type: DataTypes.STRING(64),
validate: {
is: /^[0-9a-f]{64}$/i
}
}
});
(async () => {
await sequelize.sync({ force: true });
// 这是代码
})();
验证和约束的区别
验证是在纯 JavaScript 中在 Sequelize 级别执行的检查. 如果你提供自定义验证器功能,它们可能会非常复杂,也可能是 Sequelize 提供的内置验证器之一. 如果验证失败,则根本不会将 SQL 查询发送到数据库.
另一方面,约束是在 SQL 级别定义的规则. 约束的最基本示例是唯一约束. 如果约束检查失败,则数据库将引发错误,并且 Sequelize 会将错误转发给 JavaScript(在此示例中,抛出 SequelizeUniqueConstraintError
). 请注意,在这种情况下,与验证不同,它执行了 SQL 查询.
唯一约束
下面的代码示例在 username
字段上定义了唯一约束:
/* ... */ {
username: {
type: DataTypes.TEXT,
allowNull: false,
unique: true
},
} /* ... */
同步此模型后(例如,通过调用sequelize.sync
),在表中将 username
字段创建为 `username` TEXT UNIQUE
,如果尝试插入已存在的用户名将抛出 SequelizeUniqueConstraintError
.
允许/禁止 null 值
默认情况下,null
是模型每一列的允许值. 可以通过为列设置 allowNull: false
参数来禁用它,就像在我们的代码示例的 username
字段中所做的一样:
/* ... */ {
username: {
type: DataTypes.TEXT,
allowNull: false,
unique: true
},
} /* ... */
如果没有 allowNull: false
, 那么调用 User.create({})
将会生效.
关于 allowNull
实现的说明
按照本教程开头所述,allowNull
检查是 Sequelize 中唯一由 验证 和 约束 混合而成的检查. 这是因为:
- 如果试图将
null
设置到不允许为 null 的字段,则将抛出ValidationError
,而且 不会执行任何 SQL 查询. - 另外,在
sequelize.sync
之后,具有allowNull: false
的列将使用NOT NULL
SQL 约束进行定义. 这样,尝试将值设置为null
的直接 SQL 查询也将失败.
验证器
使用模型验证器,可以为模型的每个属性指定 格式/内容/继承 验证. 验证会自动在 create
, update
和 save
时运行. 你还可以调用 validate()
来手动验证实例.
按属性验证
你可以定义你的自定义验证器,也可以使用由 validator.js (10.11.0) 实现的多个内置验证器,如下所示.
sequelize.define('foo', {
bar: {
type: DataTypes.STRING,
validate: {
is: /^[a-z]+$/i, // 匹配这个 RegExp
is: ["^[a-z]+$",'i'], // 与上面相同,但是以字符串构造 RegExp
not: /^[a-z]+$/i, // 不匹配 RegExp
not: ["^[a-z]+$",'i'], // 与上面相同,但是以字符串构造 RegExp
isEmail: true, // 检查 email 格式 (foo@bar.com)
isUrl: true, // 检查 url 格式 (https://foo.com)
isIP: true, // 检查 IPv4 (129.89.23.1) 或 IPv6 格式
isIPv4: true, // 检查 IPv4 格式 (129.89.23.1)
isIPv6: true, // 检查 IPv6 格式
isAlpha: true, // 只允许字母
isAlphanumeric: true, // 将仅允许使用字母数字,因此 '_abc' 将失败
isNumeric: true, // 只允许数字
isInt: true, // 检查有效的整数
isFloat: true, // 检查有效的浮点数
isDecimal: true, // 检查任何数字
isLowercase: true, // 检查小写
isUppercase: true, // 检查大写
notNull: true, // 不允许为空
isNull: true, // 只允许为空
notEmpty: true, // 不允许空字符串
equals: 'specific value', // 仅允许 'specific value'
contains: 'foo', // 强制特定子字符串
notIn: [['foo', 'bar']], // 检查值不是这些之一
isIn: [['foo', 'bar']], // 检查值是其中之一
notContains: 'bar', // 不允许特定的子字符串
len: [2,10], // 仅允许长度在2到10之间的值
isUUID: 4, // 只允许 uuid
isDate: true, // 只允许日期字符串
isAfter: "2011-11-05", // 仅允许特定日期之后的日期字符串
isBefore: "2011-11-05", // 仅允许特定日期之前的日期字符串
max: 23, // 仅允许值 <= 23
min: 23, // 仅允许值 >= 23
isCreditCard: true, // 检查有效的信用卡号
// 自定义验证器的示例:
isEven(value) {
if (parseInt(value) % 2 !== 0) {
throw new Error('Only even values are allowed!');
}
}
isGreaterThanOtherField(value) {
if (parseInt(value) <= parseInt(this.otherField)) {
throw new Error('Bar must be greater than otherField.');
}
}
}
}
});
请注意,在需要将多个参数传递给内置验证函数的情况下,要传递的参数必须位于数组中. 但是,如果要传递单个数组参数,例如,isIn
可接受的字符串数组,则将其解释为多个字符串参数,而不是一个数组参数. 要解决此问题,请传递一个单长度的参数数组,例如上面所示的 [['foo', 'bar']]
.
要使用自定义错误消息而不是 validator.js 提供的错误消息,请使用对象而不是纯值或参数数组,例如验证器 不需要参数就可以给自定义消息
isInt: {
msg: "必须是价格的整数"
}
或者如果还需要传递参数,则添加一个 args
属性:
isIn: {
args: [['en', 'zh']],
msg: "必须为英文或中文"
}
使用自定义验证器功能时,错误消息将是抛出的 Error
对象所持有的任何消息.
有关内置验证方法的更多详细信息,请参见validator.js 项目.
提示: 你还可以为日志记录部分定义自定义功能. 只需传递一个函数. 第一个参数是记录的字符串.