Node.js JSON Schema Ajv依赖库逐步介绍验证类型和中文错误提示

news/2025/2/19 15:12:09/

在这里插入图片描述

在构建应用程序时,数据的有效性是至关重要的。为了确保传入的数据符合预期的格式和规范,我们可以使用 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/routerkoa-bodyparser

3. 编写验证规范

我们将使用 Ajv 验证一个包含各种数据类型的 JSON 对象。以下是我们要验证的 JSON Schema:
为了添加中文注释,我们可以在 JSON Schema 的每个属性的注释中添加相关的中文描述。以下是带有中文注释的 JSON Schema:

const schema = {type: 'object',properties: {// 姓名,长度在3到20之间name: { type: 'string', minLength: 3, maxLength: 20, description: '姓名,长度在3到20之间' },// 年龄,必须是整数且不小于18age: { 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。

这个 JSON Schema 定义了一个对象,其中包含了字符串、整数、数组、邮箱、日期等各种类型的属性,并设置了一些约束条件。
如字符串、整数、数组、对象等。然而,还有一些其他可能用到的验证类型,具体取决于你的应用需求。以下是一些可能有用的额外验证类型和示例:

  1. Boolean 类型:

    const schemaWithBoolean = {type: 'object',properties: {isActive: { type: 'boolean' },},required: ['isActive'],
    };
    
  2. Null 类型:

    const schemaWithNull = {type: 'object',properties: {description: { type: 'null' },},required: ['description'],
    };
    
  3. 数字范围:

    const schemaWithNumberRange = {type: 'object',properties: {quantity: { type: 'integer', minimum: 0, maximum: 100 },},required: ['quantity'],
    };
    
  4. 字符串模式:

    const schemaWithStringPattern = {type: 'object',properties: {code: { type: 'string', pattern: '^ABC\\d{3}$' }, // 匹配以"ABC"开头,后跟三个数字的字符串},required: ['code'],
    };
    
  5. 枚举值:

    const schemaWithEnum = {type: 'object',properties: {gender: { type: 'string', enum: ['male', 'female', 'other'] },},required: ['gender'],
    };
    
  6. 数组长度:

    const schemaWithArrayLength = {type: 'object',properties: {tags: { type: 'array', minItems: 1, maxItems: 5 },},required: ['tags'],
    };
    

ajv-formats 是 Ajv 的一个插件,它提供了一些常见的格式校验,使得我们可以更方便地验证数据是否符合特定的格式要求。以下是该插件提供的一些格式校验以及它们的用法示例:

  1. date-time: 校验日期时间格式。
const schema = {type: 'string',format: 'date-time',
};// 示例数据
const validDateTime = '2022-02-14T10:30:00Z';
  1. time: 校验时间格式。
const schema = {type: 'string',format: 'time',
};// 示例数据
const validTime = '10:30:00';
  1. date: 校验日期格式。
const schema = {type: 'string',format: 'date',
};// 示例数据
const validDate = '2022-02-14';
  1. email: 校验邮箱格式。
const schema = {type: 'string',format: 'email',
};// 示例数据
const validEmail = 'example@email.com';
  1. hostname: 校验主机名格式。
const schema = {type: 'string',format: 'hostname',
};// 示例数据
const validHostname = 'www.example.com';
  1. ipv4: 校验 IPv4 地址格式。
const schema = {type: 'string',format: 'ipv4',
};// 示例数据
const validIPv4 = '192.168.0.1';
  1. ipv6: 校验 IPv6 地址格式。
const schema = {type: 'string',format: 'ipv6',
};// 示例数据
const validIPv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334';
  1. uri: 校验 URI 格式。
const schema = {type: 'string',format: 'uri',
};// 示例数据
const validURI = 'https://www.example.com';
  1. uri-reference: 校验 URI 引用格式。
const schema = {type: 'string',format: 'uri-reference',
};// 示例数据
const validURIReference = '/path/to/resource';
  1. uri-template: 校验 URI 模板格式。
const schema = {type: 'string',format: 'uri-template',
};// 示例数据
const validURITemplate = '/users/{id}';
  1. json-pointer: 校验 JSON 指针格式。
const schema = {type: 'string',format: 'json-pointer',
};// 示例数据
const validJSONPointer = '/path/to/property';
  1. relative-json-pointer: 校验相对 JSON 指针格式。
const schema = {type: 'string',format: 'relative-json-pointer',
};// 示例数据
const validRelativeJSONPointer = '1/child';
  1. regex: 校验正则表达式。
const schema = {type: 'string',format: 'regex',pattern: '^\\d{3}-\\d{2}-\\d{4}$', // 正则表达式
};// 示例数据
const validRegex = '123-45-6789';

这些格式校验使得我们在验证数据时更加灵活,能够直接使用预定义的格式进行验证,提高了数据验证的准确性和可读性。
这些是一些可能有用的验证类型和约束。根据你的具体需求,你可能需要进一步了解 JSON Schema 的其他验证选项。 JSON Schema 支持丰富的验证功能,以满足各种数据模型的需求。

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 Schemaconst validate = ajv.compile(schema);// 验证数据是否符合 JSON Schemaconst 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之间' },// 年龄,必须是整数且不小于18age: { 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 Schemaconst validate = ajv.compile(schema);// 验证数据是否符合 JSON Schemaconst 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 提供了一些组合关键字,如 allOfanyOfoneOfnot,用于在 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)

Ajv 允许你定义自己的 JSON Schema 关键字,以扩展验证功能。
在 TypeScript 环境下,你可以使用以下方式使用自定义关键字:

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

在上述示例中:

  1. 引入 Ajv 并使用 AnySchemaObject 接口。
  2. 使用 ajv.addKeyword 添加自定义关键字。
  3. 在 JSON Schema 中使用自定义关键字 eachPropIsTrue
  4. 编译 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 = '123456789012345678';
const invalidIdNumber = '1234567890'; // 不符合格式// 验证数据是否符合 JSON Schema
const validate = ajv.compile(schema);console.log(validate(validIdNumber)); // 输出 true
console.log(validate(invalidIdNumber)); // 输出 false

在这个示例中,ajv.addFormat 方法添加了一个名为 'idNumber' 的自定义格式,它使用了一个简单的正则表达式来验证身份证号码是否为18位数字。你可以根据实际需求更改验证逻辑,例如验证生日、地区等详细信息。

请注意,身份证号码的验证逻辑因国家而异,这里只是一个简单的示例。在实际应用中,你可能需要使用更复杂的验证规则来确保身份证号码的准确性和合法性。

异步验证:(Ajv compileAsync)

对于异步操作,Ajv 提供了 compileAsync 方法来编译异步的 JSON Schema。
https://ajv.js.org/guide/async-validation.html
官网示例代码 没研究明白

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 codeconst 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 invalidconsole.log("Validation errors:", err.errors)})

http://www.ppmy.cn/news/1344148.html

相关文章

ChatGPT Plus如何升级?信用卡付款失败怎么办?如何使用信用卡升级 ChatGPT Plus?

ChatGPT Plus是OpenAI提供的一种高级服务&#xff0c;它相较于标准版本&#xff0c;提供了更快的响应速度、更强大的功能&#xff0c;并且用户可以优先体验到新推出的功能。 尽管许多用户愿意支付 20 美元的月费来订阅 GPT-4&#xff0c;但在实际支付过程中&#xff0c;特别是…

pytorch——保存‘类别名与类别数量’到权值文件中

前言 不知道大家有没有像我一样&#xff0c;每换一次不一样的模型&#xff0c;就要输入不同的num_classes和name_classes,反正我是很头疼诶&#xff0c;尤其是项目里面不止一个模型的时候&#xff0c;更新的时候看着就很头疼&#xff0c;然后就想着直接输入模型权值文件的path…

STM32学习笔记三——深度讲解GPIO及其应用

目录 STM32GPIO端口位基本结构图&#xff1a; 结构图I/O引脚&#xff1a; GPIO输入输出总结 1.GPIO引脚的四种输入方式及其特点&#xff1a; 1)上拉输入(GPIO_Mode_IPU) 2)下拉输入(GPIO_Mode_IPD) 3)模拟输入(GPIO_Mode_AIN) 4)浮空输入(GPIO_Mode_IN_FLOATING…

2024.2.5 寒假训练记录(19)

文章目录 牛客 寒假集训2A Tokitsukaze and Bracelet牛客 寒假集训2B Tokitsukaze and Cats牛客 寒假集训2D Tokitsukaze and Slash Draw牛客 寒假集训2E Tokitsukaze and Eliminate (easy)牛客 寒假集训2F Tokitsukaze and Eliminate (hard)牛客 寒假集训2I Tokitsukaze and S…

vue3学习——路由

安装 pnpm i vue-router/src/router/index.ts import { createRouter, createWebHashHistory } from vue-router import { constantRoute } from /router/routes.tslet router createRouter({history: createWebHashHistory(),routes: constantRoute, })export default rout…

【issue-YOLO】自定义数据集训练YOLO-v7 Segmentation

1. 拉取代码创建环境 执行nvidia-smi验证cuda环境是否可用&#xff1b;拉取官方代码&#xff1b; clone官方代码仓库 git clone https://github.com/WongKinYiu/yolov7&#xff1b;从main分支切换到u7分支 cd yolov7 && git checkout 44f30af0daccb1a3baecc5d80eae229…

十分钟掌握Go语言==运算符与reflect.DeepEqual函数处理interface{}值的比较规则

在 Go 语言中&#xff0c;interface{} 类型是一种特殊的接口类型&#xff0c;它表示任意类型的值。你可以使用 运算符来检测任意两个 interface{} 类型值的相等性&#xff0c;比较的规则和一般的接口类型一样&#xff0c;需要满足以下条件&#xff1a; 两个 interface{} 值的…

Redis 命令大全

文章目录 启动与连接Key&#xff08;键&#xff09;相关命令String&#xff08;字符串&#xff09;Hash&#xff08;哈希&#xff09;List&#xff08;列表&#xff09;Set&#xff08;集合&#xff09;Sorted Set&#xff08;有序集合&#xff09;其他常见命令HyperLogLog&…