JSON Schema是一份用来注释和验证JSON文档开源草案,通过JSON Schema可以描述现有的数据格式,可以完成数据的自动化测试,可以有效保障数据提交的质量。
在JavaScript领域,Ajv 提供了完整的符合 JSON Schema 规范的数据校验。Ajv 的赞助商有moz://a、Microsoft等,使用Ajv的开源项目也非常多,如:ESLint、webpack等,并且在NodeJs、浏览器、桌面应用程序、微信小程序等平台均有应用。
// npm i ajv
const Ajv = require('ajv');
const ajv = new Ajv();
复制代码
const schema = {
type: 'object',
properties: {
attribute1,
attribute2,
...
},
required: ['attribute1'], // 必填属性
additionalProperties: false, // 禁止多余属性
}
复制代码
const schema = {
type: 'string',
minLength: 10, // 最小长度需满足10
}
复制代码
const schema = { type: 'number' }
复制代码
const schema = { type: 'boolean' }
复制代码
const schema = {
type: 'array',
items: {
type: 'string' // 定义数组的每一项均为字符串类型
}
}
// and
const schema = {
type: 'array',
items: [ // 定义数据包含两个元素,元素1位数字类型,元素2为字符串类型
{
type: 'number'
},{
type: 'string'
}
]
}
复制代码
运行下面的代码会同时得到三条违反规则的提示:
const Ajv = require('ajv');
const ajv = new Ajv({ allErrors: true });
const schema = {
type: 'object',
properties: {
foo: {
type: 'integer',
},
bar: {
type: 'string',
},
},
required: ['foo'],
additionalProperties: false,
};
// 执行compile后validate可以多次使用
const validate = ajv.compile(schema);
const data = {
foo: 'foo',
bar: 200,
fn1: 'fn1',
};
// 执行数据校验
const valid = validate(data);
if (!valid) {
console.log(validate.errors);
}
复制代码
从ajv7开始由ajv-formats提供属性的format工作。
// npm i ajv-formats
const addFormats = require('ajv-formats');
// ajv 实例化后添加
addFormats(ajv);
复制代码
ajv.addFormat("identifier", /^a-z\$_[a-zA-Z$_0-9]*$/)
复制代码
ajv.addFormat("byte", {
type: "number",
validate: (x) => x >= 0 && x <= 255 && x % 1 == 0,
})
复制代码
const Ajv = require('ajv');
const addFormats = require('ajv-formats');
const ajv = new Ajv({ allErrors: true });
addFormats(ajv);
const schema = {
type: 'object',
properties: {
email: {
type: 'string',
format: 'email',
},
},
};
// 执行compile后validate可以多次使用
const validate = ajv.compile(schema);
const data = {
email: '1825203636@qq.com',
};
// 执行数据校验
const valid = validate(data);
if (!valid) {
console.log(validate.errors);
}
复制代码
自定义关键字可以弥补预定义字段无法完成的验证场景,简化程序结构。 实现range关键字:
const schema = {
type: "object",
properties: {
username: { type: "string" },
password: { type: "string" },
age: { type: "number" },
// 验证邮箱的长度在 5~25 个字符之间
email: { type: "string", format: "email", range: [5, 25] },
},
required: ["username", "password"],
additionalProperties: false,
};
复制代码
通过addKeyword函数新增一个使用validate定义的关键字校验,满足在执行ajv.validate
时邮箱的字段长度符合一定的区间。
ajv.addKeyword({
keyword: "range",
validate: (schema, data) => {
if (typeof schema == "object" && schema !== null && data) {
const minLength = schema[0];
const maxLength = schema[1];
if (data.length >= minLength && data.length <= maxLength) {
return true;
}
}
return false;
},
errors: false,
});
复制代码
在ajv.compile
阶段会对关键字的类型进行校验,保证关键字的正确使用。
ajv.addKeyword({
keyword: "range",
compile([minLength, maxLength], parentSchema) {
// parentSchema:读取 schema 中更多的信息来做判断
return (data) => {
if (data.length >= minLength && data.length <= maxLength) {
return true;
}
return false;
};
},
// 规范自定义关键字的类型
metaSchema: {
type: "array",
items: [{ type: "number" }, { type: "number" }],
minItems: 2,
maxItems: 2,
additionalItems: false,
},
errors: false,
});
复制代码
通过定义macro类型关键字可以很方便的通过一个关键字来为 schema 定义批量增加数据校验。
ajv.addKeyword({
keyword: "range",
macro: ([minLength, maxLength], parentSchema) => {
return { minLength, maxLength };
},
errors: false,
});
复制代码
// npm i ajv-i18n
const localize = require("ajv-i18n");
// 校验未通过后对错误内容进行转换处理
localize.zh(validate.errors);
复制代码
const Ajv = require("ajv");
const addFormats = require("ajv-formats");
const localize = require("ajv-i18n");
const ajv = new Ajv();
addFormats(ajv);
ajv.addKeyword({
keyword: "range",
macro: ([minLength, maxLength], parentSchema) => {
return { minLength, maxLength };
},
errors: false,
});
const schema = {
type: "object",
properties: {
username: { type: "string" },
password: { type: "string" },
age: { type: "number" },
email: { type: "string", format: "email", range: [5, 15] },
},
required: ["username", "password"],
additionalProperties: false,
};
const data = {
username: "xiaoxin",
password: "xiaoxin",
email: "1825203636@qq.com",
};
const validate = ajv.compile(schema);
const valid = validate(data);
if (!valid) {
localize.zh(validate.errors);
console.log(validate.errors);
}
复制代码
// npm i ajv-errors
// ajv实例化后执行
require("ajv-errors")(ajv);
// 配置字段的新属性errorMessage,针对类型和其他关键字做不同的错误提示:
errorMessage: {
type: "必须是字符串",
minLength: "输入的用户名太短了",
}
复制代码
const Ajv = require("ajv");
const ajv = new Ajv({ allErrors: true });
require("ajv-errors")(ajv);
ajv.addKeyword({
keyword: "range",
macro: ([minLength, maxLength], parentSchema) => {
return { minLength, maxLength };
},
errors: false,
});
const schema = {
type: "object",
properties: {
username: {
type: "string",
minLength: 10,
errorMessage: {
type: "必须是字符串",
minLength: "输入的用户名太短了",
},
},
password: { type: "string" },
age: { type: "number" },
email: { type: "string", range: [5, 25] },
},
required: ["username", "password"],
additionalProperties: false,
};
const data = {
username: "xiaoxin",
password: "xiaoxin",
email: "1825203636@qq.com",
};
const validate = ajv.compile(schema);
const valid = validate(data);
if (!valid) {
console.log(validate.errors);
}
复制代码