尧图精选

基于 Ajv 的通用 JSON Schema 校验引擎:@scalar/json-schema-validator 使用指南与源码解析

🕒 发布时间:2026/9/15 22:45:57 📁 来源:尧图网络
基于 Ajv 的通用 JSON Schema 校验引擎scalar/json-schema-validator 使用指南与源码解析【免费下载链接】scalarScalar is an open-source API platform: Modern REST API Client Beautiful API References ✨ 1st-Class OpenAPI/Swagger Support项目地址: https://gitcode.com/GitHub_Trending/sc/scalar本篇文章围绕 Scalar 开源仓库中的scalar/json-schema-validator包展开。它是 Scalar 全家桶中共用的底层校验引擎负责用 JSON Schema 校验文档并输出简短、友好的人类可读错误信息同时也是scalar/openapi-validator的校验内核。读完本文你将掌握它的安装方式、validate/createValidator两大 API、方言自动识别、自定义 format 注册与throwOnError异常模式并能从源码层面理解其编译缓存、错误美化prettify与去重流水线的工作原理。包定位与 OpenAPI 无关的共享校验内核scalar/json-schema-validator在 package.json 中将自己描述为 Validate documents against a JSON Schema with Ajv and human-friendly errors。它底层依赖 Ajv 完成实际的 Schema 编译与文档校验自身则额外提供了两层能力输入宽容接受对象也接受 JSON / YAML 字符串内部使用yaml包解析错误友好把 Ajv 输出的原始错误转换成短小、可读、去重后的人类友好信息。关键设计原则是它不感知 OpenAPI 或 AsyncAPI只处理纯粹的 JSON Schema。因此任何需要按 Schema 校验 JSON 文档的场景都可以直接复用它而 Scalar 生态中的scalar/openapi-validator正是构建在这一内核之上。从 index.ts 的导出可以看到这个包对外暴露了四组能力validate/createValidator面向任意 JSON Schema 的通用校验 API来自 validate.tscreateSpecificationValidator/SpecificationValidatorConfig/SpecificationValidatorOptions面向 OpenAPI、AsyncAPI 等规范文档的高层封装器来自 create-specification-validator.tsprettifyAjvErrors/AjvError/PrettyError错误美化工具来自 prettify-ajv-errors.tsdeduplicateErrors错误去重工具来自 deduplicate-errors.ts。安装包以 npm 包scalar/json-schema-validator形式发布需要 Node.js 22 及以上见 package.json 的engines字段使用 pnpm 工作区管理属于 ES Moduletype: module。npm add scalar/json-schema-validator基础用法validate 一步到位最直接的用法是把文档和Schema同时传给validate。文档可以是对象也可以是 JSON / YAML 字符串import { validate } from scalar/json-schema-validator const schema { $schema: https://json-schema.org/draft/2020-12/schema, type: object, required: [name], properties: { name: { type: string } }, } const result validate({ name: Hello }, schema) console.log(result.valid) if (!result.valid) { console.log(result.errors) }返回的ValidationResult是判别联合类型见 types.ts校验通过时{ valid: true, errors: [] }校验失败时{ valid: false, errors: ErrorObject[] }。其中ErrorObject形如{ message: string, path?: string | string[] }——path可能是 JSON Pointer 字符串Schema 校验产生也可能是一组路径段调用方自行添加语义错误时使用。测试用例 validate.test.ts 验证了典型行为缺必填字段时错误消息形如must have required property name出现未声明属性additionalProperties: false时消息为Property extra is not expected to be here传入{ name: Hi }JSON 字符串与name: Hi\nYAML 字符串均可正常校验。方言自动识别不需要手动指定 JSON Schema 版本。校验器会从 Schema 的$schema字段自动选择方言支持draft-04http://json-schema.org/draft-04/schemadraft-07http://json-schema.org/draft-07/schema2020-12https://json-schema.org/draft/2020-12/schema这一逻辑在 validate.ts 的ajvClassesByDialect映射中实现draft-04 用ajv-draft-04包2020-12 用ajv/dist/2020.js其余默认回落到标准ajv。值得注意的一个细节是映射的 key故意去掉了末尾的#因为不同 Schema 对$schema的写法不一致OpenAPI 的 draft-04 带#AsyncAPI 的 draft-07 不带查找时会对$schema值做同样的规范化因此两种写法都能正确解析。把 YAML 字符串也当作一等输入validate内部对字符串输入统一走 YAML 解析YAML 是 JSON 的超集因此 JSON 字符串同样适用。一个贴心的设计是格式错误的字符串不会以解析器异常的形式逃逸而是被当作一次普通的校验失败——除非显式开启了throwOnError。相关测试验证了validate({ name: , schema)返回valid: false且恰好一条错误而开启throwOnError后则抛错。复用 SchemacreateValidator 一次性编译当需要对大量文档使用同一个 Schema 时反复编译 Schema 是很昂贵的。createValidator会把 Schema 编译一次返回一个可反复调用的校验函数import { createValidator } from scalar/json-schema-validator const validateUser createValidator(schema) validateUser({ name: Ada }) validateUser({ name: Grace })从 validate.ts 的实现看createValidator在调用时立即同步编译compile直接执行所以如果 Schema 本身无法编译如非法的正则pattern错误会从这个工厂函数直接抛出而不是推迟到后续的校验调用——也就是说这是装配期失败而非运行期失败validate.test.ts中专门有一条用例断言createValidator({ type: string, pattern: ( })会立即toThrow()。与之相对validate对不可信 Schema更宽容编译失败会被捕获并以valid: false的校验结果形式返回详见下文编译缓存一节。validate 背后的编译缓存validate内置了两张WeakMap缓存validate.tscompiledValidators按Schema 对象的引用身份缓存编译结果。同一个 Schema 对象再次传入时直接复用已编译的ValidateFunction不会重复编译failedCompilations缓存编译失败的 Schema 及其失败原因保证坏 Schema快速失败而不是每次调用都重付一次完整的 Ajv 装配开销。由此引出一个重要使用约束formats只在某个 Schema 对象第一次被validate看到时生效。因为后续调用复用第一次编译出的校验器即使你传入不同的formats也不会重新生效。如果需要同一 Schema 配不同 format 集合请改用createValidator分别构建见下一节。缓存还有两个边界情况值得说明布尔 SchemaJSON Schema 规范允许true/false作为合法 Schematrue表示任意值通过false表示全部拒绝但布尔值无法作为WeakMap的 key因此validate会跳过缓存直接编译validate.test.ts 有用例覆盖编译失败去重同一坏 Schema 反复开启throwOnError时抛出的错误对象是缓存中记录的同一个实例测试通过断言两次抛错引用相等thrown[1] thrown[0]来证明未重新编译。注册扩展 formatAjv 默认只认识部分内建 formatscalar/json-schema-validator通过ajv-formats注册了标准 format 集同时允许调用方通过formats选项补充自定义 formatvalidate(document, schema, { formats: { media-range: true, }, })这里的值可以是true仅做存在性检查不校验格式内容或一个 format 定义函数。实际实现中compile会先调用addFormats(ajv)注册标准 format再遍历调用方传入的formats逐项执行ajv.addFormat(name, definition)见 validate.ts。一个来自 Scalar 生态的真实例子OpenAPI 3.1 / 3.2 文档中的media-rangeformat如Accept/Content-Type头值就是通过这种方式补充注册的这一点在 types.ts 的CreateValidatorOptions注释中有明确说明。注意formats属于编译期选项CreateValidatorOptions它影响的是 Schema 如何被编译成校验器因此放在createValidator(schema, { formats })上每次新建校验器都会生效放在validate上只有该 Schema 首次编译时生效受缓存影响不要期望同一个validate调用序列里换 formats 换行为。出错即抛throwOnError默认情况下validate永不抛出对 Schema 校验、解析失败、编译失败一视同仁所有失败都以valid: false结果返回。如果你希望遇错即抛可以开启throwOnErrortry { validate(document, schema, { throwOnError: true }) } catch (error) { // Handle the first validation error }throwOnError的语义在 types.ts 中标注默认值为false并贯穿整个包的失败路径Schema 校验失败时抛出errors[0]的消息文本构造的Error文档字符串解析失败时抛出解析器原始异常Schema 编译失败时重新抛出缓存的编译错误createSpecificationValidator场景下emptyOrInvalid、versionNotSupported等前置失败同样遵循此开关。值得注意的是throwOnError抛出的第一个错误是美化、去重后的第一条错误而不是 Ajv 的原始第一条——这保证了异常信息同样可读。错误处理流水线从 Ajv 原始错误到友好消息包内错误处理遵循一条清晰的三段式流水线transform-errors.tstransformErrors → prettifyAjvErrors → deduplicateErrorstransformErrors入口封装。若输入本身是字符串则直接作为单条错误返回若文档非法如$ref解析失败导致文档为空或非对象返回Invalid specification否则调用prettifyAjvErrors并 trim 每条消息。由于畸形 Schema 可能导致美化过程自身崩溃这里包了try/catch失败时回退到原始 Ajv 错误对additionalProperties错误补充属性名。prettifyAjvErrorsprettify-ajv-errors.ts核心美化逻辑分三步建树把所有扁平错误按 JSON Pointer 的每个/segment组织成错误树。正则JSON_POINTERS_REGEX刻意用[^/]而非单词字符从而保证被转义的指针段如/paths/~1pets和带点的 key如/pet.store各自独立成节点不会因折叠而互相错误剪枝剪枝按错误具体程度排序去冗余——oneOf/anyOf/if这类容器错误只说明分支失败、不说明原因因此让位于更具体的错误required是具体且终结的缺必填属性使该对象其余错误失去意义直接胜出enum具体但较弱让位于同层更具体的错误其余type/pattern/format等一律保留。OpenAPI 中典型的oneOf: [Schema, Reference]联合导致的oneOfrequired纠缠会优先保留用户最可能想看到的required分支错误仅当唯一缺失属性是$ref即值看起来像坏引用时才回落为泛化的oneOf错误成文把过滤后的树再拉平成扁平列表按关键字逐条措辞。例如additionalProperties/unevaluatedPropertiesProperty xxx is not expected to be herepatternProperty name must match pattern ^[a-z]$能从文档解析出属性名时required沿用 Ajv 原生消息must have required property nameformat对$ref上的uri-reference专门增强——当$ref含非 ASCII 字符时输出$ref xxx contains non-ASCII characters否则输出$ref xxx is not a valid URI reference同一节点的多个enum错误会合并为一条列出所有允许值。测试 prettify-ajv-errors.test.ts 验证了这些行为例如Property 404 must match pattern ^[a-z]$OpenAPI 中responses.404这类数字对象键不会被误判为数组下标、/日本語这类非 ASCII 指针路径的保留以及/paths/~1a/get、/paths/~1b/post各自独立成节点。deduplicateErrorsdeduplicate-errors.ts以消息||路径为 key 去除 message 与 path 完全相同的重复错误。path 为数组时先join(.)归一化因此 JSON Pointer 字符串与路径段数组两种形态可以统一比较。更高一层的封装createSpecificationValidator虽然本文主角是纯 JSON Schema 校验但包内还提供了一个面向规范文档的抽象——createSpecificationValidatorcreate-specification-validator.ts它是scalar/openapi-validator等上层包与共享内核之间的桥梁也最能体现本包引擎中立的设计。它接收一个SpecificationValidatorConfig把各版本 Schema、版本探测、扩展 format、前置文档调整、后置语义检查全部收口type SpecificationValidatorConfigTVersion, TOptions { schemas: RecordTVersion, SchemaObject // 每个受支持版本的 JSON Schema detectVersion: (document) TVersion | undefined // 从解析后的文档探测规范版本 formats?: (version) Recordstring, unknown // 按版本注册额外 format errors: { emptyOrInvalid: string; versionNotSupported: string } prepareDocument?: (specification, version) AnyObject // 校验前调整文档不修改调用方副本 postValidate?: (specification, version, options) ErrorObject[] // Schema 通过后的语义检查 }返回的校验函数执行固定流程解析字符串 → 拒绝非对象文档emptyOrInvalid→ 探测版本未识别则versionNotSupported→ 按版本编译并缓存校验器每个版本只编译一次、跨调用复用→ 可选prepareDocument→ Schema 校验 → 可选postValidate语义检查。校验结果类型ValidationOutcome额外携带version与schema方便上层报告哪个版本的规范、基于什么 Schema 得出结论。从源码结构可以推断这种规范无关内核 规范相关配置的分层正是 Scalar 能同时支撑 OpenAPI 与 AsyncAPI 校验、并让两者共享同一套错误美化能力的关键架构选择。小结scalar/json-schema-validator是一个小而专的通用 JSON Schema 校验引擎以 Ajv 为内核自动识别 draft-04 / draft-07 / 2020-12 方言接受对象或 JSON/YAML 字符串输入通过编译缓存与失败缓存兼顾性能与快速失败最终用建树—剪枝—成文—去重的流水线把 Ajv 的原始错误打磨成一句句可读、可定位、无冗余的人类友好消息。无论你是要在自己的工具链里直接用它校验数据还是想理解 Scalar 的 OpenAPI / AsyncAPI 校验器为什么能输出那么干净的报错validate.ts、prettify-ajv-errors.ts 与配套测试都是极佳的研读入口。【免费下载链接】scalarScalar is an open-source API platform: Modern REST API Client Beautiful API References ✨ 1st-Class OpenAPI/Swagger Support项目地址: https://gitcode.com/GitHub_Trending/sc/scalar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →