ajv-extends
Ajv 延伸扩展。Ajv 是基于 json-schema / JTD 对数据进行验证的一个辅助库。其功能是能满足大部分的数据验证的场景的,但这个实际运用却很生草。
首先,Ajv 将初始化和错误处理(多语言),进行了分离,使得实际调用的代码异常臃肿。
如果你需要在项目环境中引用诸如 ajv-formats
ajv-i18n
,那你的代码可能会是如下:
import Ajv from 'ajv';
import addFormats from 'ajv-formats';
import localize_zh from 'ajv-i18n/localize/zh';
const ajv = new Ajv();
addFormats(ajv);
ajv.addKeyword({
keyword : 'someKeyword',
validate: () => true,
});
// 这里可能还有很多你自己项目里的扩展
// 到使用:
const schema = {
type : 'object',
properties: {
// ....
},
required : [
// ...
]
}
const validate = ajv.compile(schema);
const data = {};
const valid = validate(data);
if (!valid) {
// 将错误信息本地化
localize_zh(validate.errors);
}
其次,如果你项目中需要使用 react-hook-form
,以及 @hookform/resolver
(react-hook-form
出品的包含市面常用的 js validator 的
resolver),就会发现,@hookform/resolver/ajvResolver
是在其内部闭环的,你无法在这过程中加入其他的 ajv 扩展或者多语言本地化的处理。
可以参考这个文件:@hookform/resolver/ajv.ts
再次,ajv-i18n
的汉化,仍有需要不完善的地方,大体可以用,但是总需要在实际项目进行拦截和优化。
最后的最后,往往当我们在实际项目中添加一种 format / keyword,可能希望他能被更简单的在下一个项目重用起来,而一种 format / keyword 往往是和 localize 紧密结合的,所以需要有一个更简易的环境去使用 ajv,这也是创建这个项目的目的。
Ajv 延伸,意在由你自己决定如何扩展延伸,项目本身只提供一个简易的粘合作用,将 Ajv 的一些常用方法重新调整调用的方式,以便于人类理解使用。
主要内容
ajv.ts
该部分主要提供了一个全局简易创建和使用 Ajv 实例的快捷方式。
import { createAjv, ajv } from 'ajv-extends';
createAjv
创建一个全局统一的创建 Ajv 实例的方法
import addFormats from 'ajv-formats';
import localize_zh from 'ajv-i18n/localize/zh';
// 全局的 ajv 实例创建句柄
// 这样,通过 ajv 创建的所有 ajv 实例,都会自动套用 use / useLocalize 所添加的扩展和本地化
export const ajv = createAjv()
// 加入扩展
.use((ajvInstance, { useLocalize }) => {
// 每次创建 ajv 实例的时候会执行
addFormats(ajvInstance);
// 其他扩展
ajvInstance.addKeyword({
keyword : 'someKeyword',
error: {
message: () => 'xxx',
// => 需要将这个 keyword 的 params 进行传递,才能在本地化的时候得到这个 keyword 相应的 params
params: (cxt) => cxt.params,
},
validate: () => true,
});
useLocalize((errors) => {
if (!errors || !errors.length) return;
errors.forEach(err => {
if (err.keyword === 'someKeyword') {
err.message = 'xxx' + err.params;
}
})
});
})
// 本地化
// 只在验证时发生错误时执行
.useLocalize(localize_zh)
// 创建 ajv 实例时候的默认 options
.setOptions({ allErrors: true })
;
// 创建一个 ajv 实例
const localAjv = ajv();
// 1. 创建一个 ajv 实例,
// 2. 并 compile schema,
// 3. 然后执行 validate
// 默认模式
const { valid, errors } = ajv.validate(data, schema);
// throw error 模式
try {
ajv.validate(data, schema, true);
} catch (err) {
// TS 将所有 err 变为 unknown 类型,所以需要自行 detect
if (ajv.isValidationError(err)) {
console.log(err.errors);
}
}
ajv
为默认创建的一个全局单例 export const ajv = createAjv()
,主要用于给一些小项目直接使用(比如有些时候真的连单独创建一个 ajv
的文件都懒的时候)。但不推荐这样用(不使用他,可以不导出这个 ajv
)。
schema.ts
该部分主要为快捷创建 schema 的方法封装,目前只提供 object
类型的快捷定义
import { schemaObject } from 'ajv-extends';
schemaObject({
key: { type: 'string' },
}, ['key'])
// 支持指定泛型
// 解除了 ajv 默认的 properties 锁定的设定,即:
// 1. 强制限定 Type.field 的类型,必须和 properties 的类型强关联(实际应用中,验证器往往总是要包含更多情况)
// 2. 如果指定 Type.field 为 ? (即可能为 undefined),会强制要求对应的 properties 声明中增加 nullable
// 3. required 声明也必须限定在 Type 字段内。
// 以上三方面的限制皆去除,并且能自动根据 Type 进行代码提示。
type Type = {
field: string,
}
schemaObject<Type>(
// properties 声明
{
field: { type: 'string' },
},
// required 声明
['field'],
// additionalProperties
true,
// 其他 schema 属性
{
patternProperties: {
},
// ....
}
)