大家好,欢迎来到IT知识分享网。
在构建应用程序时,数据的有效性是至关重要的。为了确保传入的数据符合预期的格式和规范,我们可以使用 Ajv(Another JSON Schema Validator)进行验证。在这篇博文中,我们将从头开始学习 Ajv,逐步介绍验证类型和中文错误提示。
1. 什么是 Ajv?
Ajv 是一个用于验证 JSON 数据的库,它支持 JSON Schema 规范。通过定义 JSON Schema,我们可以描述数据的结构、类型和约束,然后使用 Ajv 来验证数据是否符合这些规范。
2. 安装 Ajv
首先,我们需要安装 Ajv 和一些相关的插件,打开终端并执行以下命令:
npm install ajv ajv-errors ajv-formats ajv-i18n koa @koa/router koa-bodyparser
这些插件包括错误处理插件 ajv-errors
、格式验证插件 ajv-formats
、中文错误提示插件 ajv-i18n
以及用于构建 Koa 应用的 koa
、@koa/router
和 koa-bodyparser
。
3. 编写验证规范
const schema = {
type: 'object', properties: {
// 姓名,长度在3到20之间 name: {
type: 'string', minLength: 3, maxLength: 20, description: '姓名,长度在3到20之间' }, // 年龄,必须是整数且不小于18 age: {
type: 'integer', minimum: 18, description: '年龄,必须是整数且不小于18' }, //余额,可以是浮点数 balance: {
type: "number", not: {
type: "null" } }, // 爱好,是一个字符串数组 hobbies: {
type: 'array', items: {
type: 'string' }, description: '爱好,是一个字符串数组', }, // 电子邮箱,必须符合邮箱格式 email: {
type: 'string', format: 'email', description: '电子邮箱,必须符合邮箱格式' }, // 生日,必须符合日期格式 birthday: {
type: 'string', format: 'date', description: '生日,必须符合日期格式' }, // 值,是一个包含数字和字符串的数组,且不能超过两个元素 values: {
type: 'array', items: [ {
type: 'integer', description: '第一个值是数字' }, {
type: 'string', description: '第二个值是字符串' }, ], additionalItems: false, // 防止数组包含超过两个元素 description: '值,是一个包含数字和字符串的数组,且不能超过两个元素', }, // 地址列表,是一个包含城市和邮政编码的对象数组 addresses: {
type: 'array', items: {
type: 'object', properties: {
// 城市 city: {
type: 'string', description: '城市' }, // 邮政编码 zipCode: {
type: 'string', description: '邮政编码' }, }, required: ['city', 'zipCode'], }, description: '地址列表,是一个包含城市和邮政编码的对象数组', }, }, required: ['name', 'age', 'values', 'addresses', 'birthday', 'email'], };
在这个例子中,我在每个属性的 description
中添加了中文注释,以描述该属性的含义和约束。这将有助于其他开发人员理解和维护这个 JSON Schema。
- Boolean 类型:
const schemaWithBoolean = { type: 'object', properties: { isActive: { type: 'boolean' }, }, required: ['isActive'], };
- Null 类型:
const schemaWithNull = { type: 'object', properties: { description: { type: 'null' }, }, required: ['description'], };
- 数字范围:
const schemaWithNumberRange = { type: 'object', properties: { quantity: { type: 'integer', minimum: 0, maximum: 100 }, }, required: ['quantity'], };
- 字符串模式:
const schemaWithStringPattern = { type: 'object', properties: { code: { type: 'string', pattern: '^ABC\\d{3}$' }, // 匹配以"ABC"开头,后跟三个数字的字符串 }, required: ['code'], };
- 枚举值:
const schemaWithEnum = { type: 'object', properties: { gender: { type: 'string', enum: ['male', 'female', 'other'] }, }, required: ['gender'], };
- 数组长度:
const schemaWithArrayLength = { type: 'object', properties: { tags: { type: 'array', minItems: 1, maxItems: 5 }, }, required: ['tags'], };
ajv-formats
是 Ajv 的一个插件,它提供了一些常见的格式校验,使得我们可以更方便地验证数据是否符合特定的格式要求。以下是该插件提供的一些格式校验以及它们的用法示例:
- date-time: 校验日期时间格式。
const schema = {
type: 'string', format: 'date-time', }; // 示例数据 const validDateTime = '2022-02-14T10:30:00Z';
- time: 校验时间格式。
const schema = {
type: 'string', format: 'time', }; // 示例数据 const validTime = '10:30:00';
- date: 校验日期格式。
const schema = {
type: 'string', format: 'date', }; // 示例数据 const validDate = '2022-02-14';
- email: 校验邮箱格式。
const schema = {
type: 'string', format: 'email', }; // 示例数据 const validEmail = '';
- hostname: 校验主机名格式。
const schema = {
type: 'string', format: 'hostname', }; // 示例数据 const validHostname = 'www.example.com';
- ipv4: 校验 IPv4 地址格式。
const schema = {
type: 'string', format: 'ipv4', }; // 示例数据 const validIPv4 = '192.168.0.1';
- ipv6: 校验 IPv6 地址格式。
const schema = {
type: 'string', format: 'ipv6', }; // 示例数据 const validIPv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334';
- uri: 校验 URI 格式。
const schema = {
type: 'string', format: 'uri', }; // 示例数据 const validURI = 'https://www.example.com';
- uri-reference: 校验 URI 引用格式。
const schema = {
type: 'string', format: 'uri-reference', }; // 示例数据 const validURIReference = '/path/to/resource';
- uri-template: 校验 URI 模板格式。
const schema = {
type: 'string', format: 'uri-template', }; // 示例数据 const validURITemplate = '/users/{id}';
- json-pointer: 校验 JSON 指针格式。
const schema = {
type: 'string', format: 'json-pointer', }; // 示例数据 const validJSONPointer = '/path/to/property';
- relative-json-pointer: 校验相对 JSON 指针格式。
const schema = {
type: 'string', format: 'relative-json-pointer', }; // 示例数据 const validRelativeJSONPointer = '1/child';
- regex: 校验正则表达式。
const schema = {
type: 'string', format: 'regex', pattern: '^\\d{3}-\\d{2}-\\d{4}$', // 正则表达式 }; // 示例数据 const validRegex = '123-45-6789';
4. 中间件:验证请求数据
接下来,我们将创建一个 Koa 中间件来验证请求数据是否符合上述定义的 JSON Schema。在中间件中,我们使用 Ajv 编译 JSON Schema 并验证请求数据:
const Ajv = require('ajv'); const localize = require('ajv-i18n'); const ajv = new Ajv({
allErrors: true }); require('ajv-errors')(ajv); require('ajv-formats')(ajv); // 中间件:校验请求数据 const validateMiddleware = async (ctx, next) => {
const data = ctx.request.body; // 编译 JSON Schema const validate = ajv.compile(schema); // 验证数据是否符合 JSON Schema const isValid = validate(data); if (!isValid) {
// 指定语言为中文 localize.zh(validate.errors); // 设置 errorsText 选项为中文 const errorText = ajv.errorsText(validate.errors, {
separator: '\n', dataVar: 'data' }); ctx.status = 400; ctx.body = {
error: 'Invalid data', details: errorText, }; return; } await next(); };
在这个中间件中,我们使用 ajv.errorsText
来生成错误文本,同时将 dataVar
选项设置为 'data'
,以确保在错误消息中使用中文。如果数据验证不通过,中间件将返回包含中文错误信息的 400 错误响应。
5. 应用 Koa 路由
最后,我们将创建一个 Koa 应用,使用上述中间件处理 /api/data
路由的 POST 请求:
const Koa = require('koa'); const Router = require('@koa/router'); const bodyParser = require('koa-bodyparser'); const app = new Koa(); const router = new Router(); // 使用中间件 app.use(bodyParser()); // 路由处理 router.post('/api/data', validateMiddleware, async (ctx) => {
ctx.body = {
message: 'Data is valid!' }; }); // 添加路由 app.use(router.routes()); app.use(router.allowedMethods()); // 启动应用 const PORT = 3000; app.listen(PORT, () => {
console.log(`Server is running on port ${
PORT}`); });
现在,我们的 Koa 应用已经可以验证请求数据,并返回相应的中文错误信息了。
通过这篇博文,我们逐步学习了如何使用 Ajv 验证不同类型的数据,并在 Koa 应用中实现中文错误提示。这为构建健壮的应用程序提供了强大的数据验证工具。
完整验证接口数据示例代码
const Koa = require('koa'); const Router = require('@koa/router'); const bodyParser = require('koa-bodyparser'); const Ajv = require('ajv'); const localize = require("ajv-i18n"); const ajv = new Ajv({
allErrors: true }); require('ajv-errors')(ajv); require('ajv-formats')(ajv); const app = new Koa(); const router = new Router(); // 定义 JSON Schema const schema = {
type: 'object', properties: {
// 姓名,长度在3到20之间 name: {
type: 'string', minLength: 3, maxLength: 20, description: '姓名,长度在3到20之间' }, // 年龄,必须是整数且不小于18 age: {
type: 'integer', minimum: 18, description: '年龄,必须是整数且不小于18' }, // 爱好,是一个字符串数组 hobbies: {
type: 'array', items: {
type: 'string' }, description: '爱好,是一个字符串数组', }, // 电子邮箱,必须符合邮箱格式 email: {
type: 'string', format: 'email', description: '电子邮箱,必须符合邮箱格式' }, // 生日,必须符合日期格式 birthday: {
type: 'string', format: 'date', description: '生日,必须符合日期格式' }, // 值,是一个包含数字和字符串的数组,且不能超过两个元素 values: {
type: 'array', items: [ {
type: 'integer', description: '第一个值是数字' }, {
type: 'string', description: '第二个值是字符串' }, ], additionalItems: false, // 防止数组包含超过两个元素 description: '值,是一个包含数字和字符串的数组,且不能超过两个元素', }, // 地址列表,是一个包含城市和邮政编码的对象数组 addresses: {
type: 'array', items: {
type: 'object', properties: {
// 城市 city: {
type: 'string', description: '城市' }, // 邮政编码 zipCode: {
type: 'string', description: '邮政编码' }, }, required: ['city', 'zipCode'], }, description: '地址列表,是一个包含城市和邮政编码的对象数组', }, }, required: ['name', 'age', 'values', 'addresses', 'birthday', 'email'], }; // 中间件:校验请求数据 const validateMiddleware = async (ctx, next) => {
const data = ctx.request.body; // 编译 JSON Schema const validate = ajv.compile(schema); // 验证数据是否符合 JSON Schema const isValid = validate(data); if (!isValid) {
// 指定语言为中文 localize.zh(validate.errors); // 设置 errorsText 选项为中文 const errorText = ajv.errorsText(validate.errors, {
separator: '\n', dataVar: 'data' }); ctx.status = 400; ctx.body = {
error: 'Invalid data', details:errorText, }; return; } await next(); }; // 使用中间件 app.use(bodyParser()); // 路由处理 router.post('/api/data', validateMiddleware, async (ctx) => {
ctx.body = {
message: '数据验证通过' }; }); // 添加路由 app.use(router.routes()); app.use(router.allowedMethods()); // 启动应用 const PORT = 3000; app.listen(PORT, () => {
console.log(`Server is running on port ${
PORT}`); });
HTTP 请求示例数据
POST /api/data HTTP/1.1 Host: 127.0.0.1:3000 Content-Type: application/json Content-Length: 313 {
"name":"xiongmingcai", "email":"xiongmingcai(#)gmail.com", "age":30, "birthday":"1990-05-15", "hobbies":["唱跳","RAP","打篮球"], "values":[1,"字符串"], "addresses":[{
"city":"北京", "zipCode":"000000" },{
"city":"长沙", "zipCode":"000000" } ] }
进阶用法 使用 $ref 引用其他 JSON Schema
const mainSchema = {
$id: 'mainSchema', type: 'object', properties: {
person: {
$ref: 'personSchema' }, address: {
$ref: 'addressSchema' }, }, }; const personSchema = {
$id: 'personSchema', type: 'object', properties: {
name: {
type: 'string' }, age: {
type: 'integer' }, }, required: ['name', 'age'], }; const addressSchema = {
$id: 'addressSchema', type: 'object', properties: {
city: {
type: 'string' }, zipCode: {
type: 'string' }, }, required: ['city', 'zipCode'], }; const ajv = new Ajv(); ajv.addSchema([mainSchema, personSchema, addressSchema]); const validateMain = ajv.getSchema('mainSchema'); const data = {
person: {
name: 'John', age: 25 }, address: {
city: 'New York', zipCode: '10001' }, }; const isValid = validateMain(data); if (isValid) {
console.log('Data is valid!'); } else {
console.error('Data is invalid!'); console.error(validateMain.errors); }
组合关键字(AJV allOf、anyOf、oneOf、not)
AJV 提供了一些组合关键字,如 allOf
、anyOf
、oneOf
和 not
,用于在 JSON Schema 中表示更复杂的逻辑关系。以下是这些关键字的使用示例:
1. allOf
:所有条件都必须匹配
import Ajv from 'ajv'; // 创建 Ajv 实例 const ajv = new Ajv(); // 定义 JSON Schema 使用 allOf const schema = {
allOf: [ {
type: 'object', required: ['name'] }, {
type: 'object', properties: {
age: {
type: 'number' } } }, ], }; // 示例数据 const validData = {
name: 'John', age: 25 }; // 验证数据是否符合 JSON Schema const validate = ajv.compile(schema); const isValid = validate(validData); console.log(isValid); // 输出 true
2. anyOf
:至少一个条件匹配
import Ajv from 'ajv'; // 创建 Ajv 实例 const ajv = new Ajv(); // 定义 JSON Schema 使用 anyOf const schema = {
anyOf: [ {
type: 'object', required: ['name'] }, {
type: 'object', properties: {
age: {
type: 'number' } } }, ], }; // 示例数据 const validData = {
name: 'John' }; // 验证数据是否符合 JSON Schema const validate = ajv.compile(schema); const isValid = validate(validData); console.log(isValid); // 输出 true
3. oneOf
:只有一个条件匹配
import Ajv from 'ajv'; // 创建 Ajv 实例 const ajv = new Ajv(); // 定义 JSON Schema 使用 oneOf const schema = {
oneOf: [ {
type: 'object', required: ['name'] }, {
type: 'object', properties: {
age: {
type: 'number' } } }, ], }; // 示例数据 const validData = {
name: 'John' }; // 验证数据是否符合 JSON Schema const validate = ajv.compile(schema); const isValid = validate(validData); console.log(isValid); // 输出 true
4. not
:条件不能匹配
import Ajv from 'ajv'; // 创建 Ajv 实例 const ajv = new Ajv(); // 定义 JSON Schema 使用 not const schema = {
not: {
type: 'object', properties: {
age: {
type: 'number' } }, }, }; // 示例数据 const invalidData = {
age: 25 }; // 验证数据是否符合 JSON Schema const validate = ajv.compile(schema); const isValid = validate(invalidData); console.log(isValid); // 输出 false
这些关键字允许你构建更复杂的验证规则,以满足特定的数据结构和逻辑需求。在实际应用中,可以根据具体情况组合使用这些关键字。。
如何设置自定义AJV关键字?( custom AJV keyword)
import Ajv, {
AnySchemaObject } from 'ajv'; // 创建 Ajv 实例 const ajv = new Ajv(); // 自定义关键字定义 let kwdOrDef: FuncKeywordDefinition = {
keyword: 'eachPropIsTrue', // 关键字的名称 type: 'object', // 关键字适用的 JSON 数据类型 schemaType: 'boolean', // 关键字的 schema 类型 compile: (schema: boolean, parentSchema: AnySchemaObject) => {
// 编译函数,用于生成验证函数 return (data: Record<string, any>) => {
// 验证函数逻辑 return Object.values(data).every((value) => !!value); }; }, }; // 添加自定义关键字到 Ajv 实例中 ajv.addKeyword(kwdOrDef); // 定义 JSON Schema const schema = {
type: 'object', eachPropIsTrue: true, }; // 示例数据 const validData = {
prop1: true, prop2: false, prop3: true, }; // 验证数据是否符合 JSON Schema const validate = ajv.compile(schema); const isValid = validate(validData); console.log(isValid); // 输出 false,因为 prop2 是 false
在上述示例中:
- 引入
Ajv
并使用AnySchemaObject
接口。 - 使用
ajv.addKeyword
添加自定义关键字。 - 在 JSON Schema 中使用自定义关键字
eachPropIsTrue
。 - 编译 JSON Schema 并验证数据是否符合。
这样,你就可以在 TypeScript 环境下使用自定义关键字了。确保在编写 TypeScript 代码时,按照 TypeScript 的语法规范进行书写。
自定义格式(custom AJV format)
要在 Ajv 中添加自定义格式来验证身份证号码,你需要使用 ajv.addFormat
方法并提供一个验证函数。以下是一个简单的示例,演示了如何验证身份证号码的基本格式:
import Ajv from 'ajv'; // 创建 Ajv 实例 const ajv = new Ajv(); // 添加身份证号码格式验证 ajv.addFormat('idNumber', (data) => {
// 简单示例:验证身份证号码为18位数字 const regex = /^[0-9]{18}$/; return regex.test(data); }); // 定义 JSON Schema const schema = {
type: 'string', format: 'idNumber', }; // 示例数据 const validIdNumber = ''; const invalidIdNumber = ''; // 不符合格式 // 验证数据是否符合 JSON Schema const validate = ajv.compile(schema); console.log(validate(validIdNumber)); // 输出 true console.log(validate(invalidIdNumber)); // 输出 false
在这个示例中,ajv.addFormat
方法添加了一个名为 'idNumber'
的自定义格式,它使用了一个简单的正则表达式来验证身份证号码是否为18位数字。你可以根据实际需求更改验证逻辑,例如验证生日、地区等详细信息。
请注意,身份证号码的验证逻辑因国家而异,这里只是一个简单的示例。在实际应用中,你可能需要使用更复杂的验证规则来确保身份证号码的准确性和合法性。
异步验证:(Ajv compileAsync)
const ajv = new Ajv() ajv.addKeyword({
keyword: "idExists", async: true, type: "number", validate: checkIdExists, }) async function checkIdExists(schema, data) {
// this is just an example, you would want to avoid SQL injection in your code const rows = await sql(`SELECT id FROM ${
schema.table} WHERE id = ${
data}`) return !!rows.length // true if record is found } const schema = {
$async: true, properties: {
userId: {
type: "integer", idExists: {
table: "users"}, }, postId: {
type: "integer", idExists: {
table: "posts"}, }, }, } const validate = ajv.compile(schema) validate({
userId: 1, postId: 19}) .then(function (data) {
console.log("Data is valid", data) // { userId: 1, postId: 19 } }) .catch(function (err) {
if (!(err instanceof Ajv.ValidationError)) throw err // data is invalid console.log("Validation errors:", err.errors) })
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/125329.html