@rjsf/validator-ata 完全指南:为 react-jsonschema-form 接入 ata-validator 校验引擎
【免费下载链接】react-jsonschema-formA React component for building Web forms from JSON Schema.项目地址: https://gitcode.com/gh_mirrors/re/react-jsonschema-form
@rjsf/validator-ata是 react-jsonschema-form(RJSF)生态中一个基于ata-validator)为主体骨架,结合packages/validator-ata的源码与配套的验证指南,完整讲解其公共 API(customizeValidator、compileSchemaValidators、createPrecompiledValidator)、与 AJV8 校验器的差异、预编译校验(Precompiled Validator)工作流,以及底层实现原理,帮助你快速完成迁移、定制与排障。
一、它是什么:一行 import 完成的校验器替换
自 RJSF v5 起,校验实现已从Form组件中解耦,所有Form必须显式传入一个实现了ValidatorType<T, S, F>接口(定义于@rjsf/utils)的校验器实例。@rjsf/validator-ata正是这样一份实现,其公共 API 表面与 AJV8 校验包完全对齐,因此:
// 之前 import validator from '@rjsf/validator-ajv8'; // 之后 import validator from '@rjsf/validator-ata';仅替换 import 即可让表单继续工作。文档明确承诺以下能力在两个包中行为一致:
customizeValidator()定制入口;ValidatorType<T, S, F>类型接口;- 自定义格式(custom formats);
transformErrors错误变换;customValidate自定义校验;suppressDuplicateFiltering重复错误过滤。
从源码入口 packages/validator-ata/src/index.ts 可以看到,该包导出customizeValidator、createPrecompiledValidator、ATAValidator类及全部类型,并以export default customizeValidator()作为默认导出,与 AJV8 包的默认导出形态一致。
配套的完整校验用法示例(
liveValidate、customValidate、transformErrors、noHtml5Validate、extraErrors等)见仓库的验证文档,本文重点聚焦 validator-ata 自身的 API 与实现。
二、与 @rjsf/validator-ajv8 的差异
官方文档列出了四个关键差异,理解它们有助于评估迁移成本:
- 不接受 AJV 专属选项:
AjvClass、ajvFormatOptions、ajvOptionsOverrides在 ata 版中均不可用。最接近的等价物是ataOptionsOverrides,它会被浅展开(spread)到默认的ata-validator选项之上。这一点在 types.ts 的类型注释中有明确说明:与 AJV 无对应映射的旋钮被有意省略。 - 格式集总是安装:
ata-validator自带格式集始终生效,没有 AJV 版那种通过ajvFormatOptions控制格式安装的开关。从 createAtaInstance.ts 的实现看,ata 实例构造时会无条件注册 RJSF 依赖的color与data-url两个内置格式(COLOR_FORMAT_REGEX与DATA_URL_FORMAT_REGEX与 AJV8 包保持一致),再叠加用户传入的customFormats。 - 错误参数被冻结:
ata-validator的error.params是冻结(frozen)对象,因此 AJV8 版为配合ajv-i18n而做的"预引号处理"(pre-quote pass)不再需要。凡是采用原地修改message方式的本地化器(localizer)依然可以直接使用。对应实现见 validator.ts:localizer直接以原始 ata 错误数组为参数被调用。 - 预编译校验器同样受支持:
compileSchemaValidators/createPrecompiledValidator均可用。ata-validator的bundleStandalone输出被适配为预编译消费者期望的"校验函数映射"。错误输出与非预编译路径一致,包括:- 对 schema 形态的
additionalProperties产生逐字段错误(需要ata-validator >= 0.17.4;当前仓库依赖^1.7.1,满足该要求,见 package.json); - 非法的
anyOf与运行时路径一致,只在字段上报告单个错误; - 从 standalone(AOT)路径继承的约束:自定义格式必须是 RegExp 或预锚定(pre-anchored)的字符串模式,因为函数形式的检查器无法序列化进 bundle,会在编译期被拒绝。
- 对 schema 形态的
三、类型体系
@rjsf/validator-ata导出了一组支撑上述 API 的 TypeScript 类型,完整定义见 packages/validator-ata/src/types.ts,核心包括:
| 类型 | 说明 |
|---|---|
AtaFormatChecker | (value: string) => boolean形状的自定义格式检查器,与ata-validator的formats选项对齐 |
SuppressDuplicateFilteringType | 'anyOf' \| 'oneOf' \| 'all' \| 'none',控制哪些关键字跳过重复错误过滤 |
Localizer | 接收ata-validator的ValidationError列表并原地修改/替换消息的本地化函数(注意与 AJV 版的ErrorObject[]输入类型不同) |
CustomValidatorOptionsType | customizeValidator()的选项映射,见下文第四节 |
CompiledValidateFunction | 预编译模块中单个校验函数的简化形态:(data: unknown) => boolean,并挂载errors属性 |
ValidatorFunctions | Record<string, CompiledValidateFunction>,即预编译文件导出的校验函数映射 |
其中CustomValidatorOptionsType的字段与语义如下(types.ts):
additionalMetaSchemas?: readonly object[]:额外注册的 schema,用于跨 schema 的$ref解析,语义对应 AJV 的addMetaSchema/addSchema;customFormats?: Record<string, string \| RegExp \| AtaFormatChecker>:自定义格式检查器,值可以是函数、RegExp 或(会被编译为函数的)预锚定正则源码字符串;ataOptionsOverrides?: ValidatorOptions:覆盖默认ata-validator选项,例如coerceTypes、removeAdditional、verbose、abortEarly;extenderFn?: (validator: Validator) => Validator:对刚构造的Validator实例做额外设置的回调,允许返回不同实例;suppressDuplicateFiltering?: SuppressDuplicateFilteringType:见下文。
四、customizeValidator():构建定制校验器
customizeValidator<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>( options?: CustomValidatorOptionsType, localizer?: Localizer, ): ValidatorType<T, S, F>创建并返回给定定制选项下的ValidatorType实现。如果提供了localizer,它会被用来翻译底层ata-validator校验生成的错误消息。
4.1 选项详解
additionalMetaSchemas:用于跨 schema$ref解析的附加 schema。在 createAtaInstance.ts 中,这些 meta schema 会逐个调用validator.addSchema(meta)注册。customFormats:自定义格式检查器。三种取值形态在 asFormatChecker 中被统一归一为(value: string) => boolean:函数直接透传,RegExp 包装成.test(value)调用,字符串被视为正则源码构造new RegExp(spec)。ataOptionsOverrides:展开到默认选项之上。默认选项ATA_CONFIG只有一项(createAtaInstance.ts):verbose: true。这是因为verbose会让错误对象保留parentSchema,而 RJSF 的错误转换器依赖它恢复字段标题(title);其余默认行为(等价于 AJV 的allErrors)由 ata 自身默认提供。extenderFn:构造完Validator实例后立即调用,可在此接入第三方增强(类似 AJV 生态的ajv-errors/ajv-keywords),返回值支持替换原实例。suppressDuplicateFiltering:控制anyOf/oneOf重复错误的过滤,取值语义如下表(该表亦适用于 AJV8 包):
| 值 | 行为 |
|---|---|
'none'(默认) | anyOf与oneOf的重复错误都被过滤 |
'anyOf' | 关闭anyOf的重复过滤;oneOf仍过滤 |
'oneOf' | 关闭oneOf的重复过滤;anyOf仍过滤 |
'all' | 关闭全部重复过滤,返回每个分支产生的每条错误 |
过滤逻辑的底层实现位于 processRawValidationErrors.ts 的filterDuplicateErrors:在非'all'模式下,对schemaPath中含/anyOf/或/oneOf/段的错误,凡是在该段之前前缀相同且消息相同的,只保留第一条。
4.2 localizer(本地化)
第二个参数localizer的类型为Localizer,即(errors?: null | ValidationError[]) => void。由于 ata 的error.params被冻结,AJV8 版为ajv-i18n准备的预引号处理无法照搬,但任何原地修改message的本地化函数都能直接工作。例如一个自定义俄语本地化器(对照 validation.md 中的 AJV 版示例,输入错误类型改为 ata 的ValidationError):
import { Form } from '@rjsf/core'; import { RJSFSchema } from '@rjsf/utils'; import { customizeValidator } from '@rjsf/validator-ata'; import type { ValidationError } from 'ata-validator'; function localize_ru(errors: null | ValidationError[] = []) { if (!(errors && errors.length)) return; errors.forEach((error) => { switch (error.keyword) { case 'pattern': error.message = 'должно соответствовать образцу "' + error.params.pattern + '"'; break; case 'required': error.message = 'поле обязательно для заполнения'; break; default: break; // 保持原始 message } }); } const schema: RJSFSchema = { type: 'string' }; const validator = customizeValidator({}, localize_ru); render(<Form schema={schema} validator={validator} />, document.getElementById('app'));要点:必须原地修改列表并自行覆盖所有需要处理的关键字分支。
4.3 组合使用示例
import { Form } from '@rjsf/core'; import { RJSFSchema } from '@rjsf/utils'; import { customizeValidator } from '@rjsf/validator-ata'; const schema: RJSFSchema = { type: 'string', format: 'phone-us', }; const validator = customizeValidator({ customFormats: { 'phone-us': /\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{4}$/, }, ataOptionsOverrides: { coerceTypes: true, // 允许类型强制转换 verbose: true, // 保留 parentSchema 供错误标题解析 }, suppressDuplicateFiltering: 'all', // 展示 anyOf/oneOf 所有分支错误 }); render(<Form schema={schema} validator={validator} />, document.getElementById('app'));五、预编译校验器:绕过 CSP 与启动期编译
预编译校验器(Precompiled Validator)的核心动机有三个:减小打包体积、通过跳过 schema 编译提升启动速度,以及在浏览器 Content Security Policy(CSP)禁止动态代码生成时避免运行时unsafe-eval。工作流分两步:
- 用
compileSchemaValidators()把 schema 预编译为 CommonJS 模块文件; - 用
createPrecompiledValidator()把编译产物包装成ValidatorType交给Form。
5.1 compileSchemaValidators():schema 预编译
compileSchemaValidators<S extends StrictRJSFSchema = RJSFSchema>( schema: S, output: string, options?: CustomValidatorOptionsType, ): void把schema编译进名为output的输出文件中,之后可作为预编译校验器加载。options与customizeValidator()接受同一组CustomValidatorOptionsType,用于影响编译时使用的 ata 校验器。
典型用法(官方推荐建一个compileYourSchema.js后用 node 运行):
const compileSchemaValidators = require('@rjsf/validator-ata/compileSchemaValidators').default; const yourSchema = require('path_to/yourSchema'); // 若 schema 是 js 文件 compileSchemaValidators(yourSchema, 'path_to/yourCompiledSchema.js');带定制选项的版本:
const { compileSchemaValidators } = require('@rjsf/validator-ata'); const yourSchema = require('path_to/yourSchema.json'); // 若 schema 是 json 文件 const options = { additionalMetaSchemas: [/* 需要注册的附加 meta schema */], customFormats: { 'phone-us': /\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{4}$/, }, ataOptionsOverrides: { verbose: true, }, }; compileSchemaValidators(yourSchema, 'path_to/yourCompiledSchema.js', options);然后执行:
node compileYourSchema.js实现细节:该函数先调用compileSchemaValidatorsCode(schema, options)生成模块代码,再用fs.writeFileSync(output, moduleCode)落盘(compileSchemaValidators.ts)。包入口已通过 package.json 的exports暴露./compileSchemaValidators子路径(package.json),同时支持require(CJS)与import(ESM)。
重要限制:通过
options.customFormats传入的自定义格式必须是RegExp 或预锚定的字符串模式。函数检查器无法序列化进 standalone bundle,会在编译期被拒绝(抛出明确错误)。
该限制的源码依据在 compileSchemaValidatorsCode.ts:函数形式会被Function#toString序列化,而转译器、压缩器与覆盖率工具会改写函数体并留下生成模块中不存在的引用,因此只有 RegExp/字符串模式能被可靠内嵌。
5.2 createPrecompiledValidator():加载预编译产物
createPrecompiledValidator<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>( validateFns: ValidatorFunctions, rootSchema: S, localizer?: Localizer, suppressDuplicateFiltering?: SuppressDuplicateFilteringType, ): ValidatorType<T, S, F>validateFns:由compileSchemaValidators()生成的预编译文件经 import 得到的函数映射对象;rootSchema:编译时使用的同一个根 schema;localizer:可选,用于翻译 ataValidationError列表;suppressDuplicateFiltering:可选,语义与customizeValidator()相同(见 4.1 表格)。
在Form中使用:
import { useMemo } from 'react'; import Form, { FormProps } from '@rjsf/core'; // 或任意主题包 import { createPrecompiledValidator } from '@rjsf/validator-ata'; import yourSchema from 'path_to/yourSchema'; // 必须与编译时是同一文件 import * as precompiledValidatorFns from 'path_to/yourCompiledSchema'; function MyForm(props: Omit<FormProps, 'validator' | 'schema'>) { // 记忆化校验器,避免重复渲染时重建 const validator = useMemo( () => createPrecompiledValidator(precompiledValidatorFns, yourSchema), [precompiledValidatorFns, yourSchema], ); return <Form schema={yourSchema} validator={validator} {...props} />; }注意:validateFns必须是compileSchemaValidators()产物的 import 结果。若传入的 schema 与构造ATAPrecompiledValidator时的根 schema 不一致,ensureSameRootSchema会抛出错误;若映射中找不到对应 schema 的校验函数,getValidator同样会抛错(precompiledValidator.ts)。
5.3 动态预编译:compileSchemaValidatorsCode
对于"按请求动态预编译"的高级场景,可以改用compileSchemaValidatorsCode(schema, options)——它与compileSchemaValidators逻辑相同,但不写文件,直接返回生成的代码字符串:
import { compileSchemaValidatorsCode } from '@rjsf/validator-ata/compileSchemaValidators'; const code = compileSchemaValidatorsCode(schema, options);在浏览器端使用该代码时,需要为生成代码中的运行时依赖提供替代实现(其机制与 AJV 包的动态预编译流程一致,可参考 validation.md 中evaluateValidator与替换映射的完整示例)。
六、源码级原理:ATAValidator 如何工作
customizeValidator()的实现非常薄(customizeValidator.ts):它只是new ATAValidator<T, S, F>(options, localizer)。真正的逻辑都在ATAValidator类中(validator.ts),理解以下几点可以解释文档中大部分行为差异:
6.1 与 AJV 的架构差异:schema 绑定 vs 实例注册表
AJV 是"单实例 + schema 注册表"模型;而ata 是 schema 绑定(schema-bound)模型,每个 schema 对应一个Validator实例。因此ATAValidator内部维护了一个按 schema id 索引的缓存:private readonly validators = new Map<string, { validator: Validator; schema: object }>()。缓存键优先取 schema 的$id(ID_KEY),否则用hashForSchema生成的哈希(validator.ts)。
当 RJSF 传入新的根 schema 时,handleSchemaUpdate会把根 schema 注册进 ata 的 schema 集合,使子 schema 校验时$ref仍能解析到根级definitions(validator.ts)。reset()方法清空缓存与根 schema 记账,供 RJSF 测试框架在多次运行间刷新状态。
6.2 防篡改克隆:cloneForValidation
ata 的默认值应用器在校验过程中会把default值写回传入的数据对象。而 RJSF 在解析oneOf/anyOf分支时会通过isValid反复探测同一份数据,被篡改的探测结果会改变后续答案。为此ATAValidator在每次校验前用structuredClone深拷贝数据(validator.ts),以保持 AJV 默认具备的引用纯净性。
6.3 错误处理管线
无论运行时路径还是预编译路径,最终都汇入 processRawValidationErrors.ts 的同一套管线:
transformRJSFValidationErrors将 ata 错误结构化为 RJSF 的RJSFValidationError(ata 错误字段与 AJV 同名:instancePath、keyword、params、schemaPath、parentSchema、message,因此转换是结构性的),并依据 uiSchema/parentSchema/根 schema 中的title替换消息中的属性名;filterDuplicateErrors按suppressDuplicateFiltering折叠anyOf/oneOf重复错误;- 追加 schema 编译错误(若存在)、调用
transformErrors; toErrorSchema构建errorSchema;- 若有
customValidate,先getDefaultFormState补齐默认值,再合并用户错误。
这也印证了文档中的承诺:customValidate、transformErrors、suppressDuplicateFiltering等能力在两个校验包中行为一致。
七、迁移速查与注意事项
把现有 AJV8 校验器迁移到 validator-ata 时,按以下清单自查:
- 替换 import:
@rjsf/validator-ajv8→@rjsf/validator-ata;预编译工具路径对应改为@rjsf/validator-ata/compileSchemaValidators。 - 改写选项名:
ajvOptionsOverrides→ataOptionsOverrides;删除AjvClass、ajvFormatOptions(ata 格式集始终安装,无需开关)。 - 检查格式定义:运行时路径的
customFormats支持函数/RegExp/字符串三种形态;预编译路径只接受 RegExp 或预锚定字符串,函数会在编译期直接报错。 - 检查本地化器:只要你的 localizer 是原地修改
message,无需改动即可继续使用;无需为ajv-i18n做预引号处理。 - 确认版本前提:若使用 schema 形态的
additionalProperties并期望逐字段错误,需保证ata-validator >= 0.17.4(当前仓库锁定^1.7.1)。 - 验证错误输出:非法的
anyOf只报告字段级单条错误,与运行时路径一致;如需查看每个分支的全部错误,使用suppressDuplicateFiltering: 'all'。
如果需要进一步了解liveValidate、customValidate、transformErrors、extraErrors、错误列表模板等与校验相关的完整表单用法,可继续阅读仓库的验证文档;所有 API 的最终权威定义位于 packages/validator-ata/src 目录,测试用例可参考 packages/validator-ata/test 下的validator.test.ts、customizeValidator.test.ts、precompiledValidator.test.ts等文件。
【免费下载链接】react-jsonschema-formA React component for building Web forms from JSON Schema.项目地址: https://gitcode.com/gh_mirrors/re/react-jsonschema-form
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考