← 返回文章列表

用 JSON Schema 给接口入参把关

JSONAPI入门

为什么要在边界校验

信任边界处校验一次,比在每个函数里到处判空更省心。JSON Schema 把「这个字段必须存在、必须是数字、范围是多少」写成一份可机器校验的契约。

常用关键字

关键字作用
required必填字段
type基础类型(string/number/object/array…)
format语义格式(email/date-time/uuid)
minimum / maximum数值范围
pattern正则约束

两个坑

  • Schema 不是安全边界:它挡不住业务逻辑漏洞和注入,SQL 注入仍要靠参数化查询;
  • 别只校验类型:字符串长度、数组上限也要限,否则可能撑爆存储或内存。

动手试试

格式化与检查 JSON:JSON 格式化。

实战案例:三个 schema 没拦住的脏数据

  1. 只校验类型、没校验格式:type: string 拦不住空串或错误邮箱,应补 format、minLength 与 pattern。
  2. 放过了未声明字段:默认允许额外字段,客户端多传的内容会被静默写库。设 additionalProperties: false 能尽早暴露契约不一致。
  3. 共享 schema 而没有版本:复用 $ref 的 schema 一改,所有依赖方同时受影响。给 schema 加版本号,变更走兼容评审。

常见问题(FAQ)

JSON Schema 能替代业务校验吗?不能,它只管结构;跨字段规则(如结束时间必须晚于开始时间)仍要业务代码。校验失败怎么返回更好?返回字段路径与原因列表,便于前端定位,别只给一句“参数错误”。前端校验就够了吗?不够,前端校验只是体验优化,服务端必须独立校验。oneOf 和 anyOf 怎么选?需要知道“命中了哪个分支”时用 oneOf,纯结构约束用 anyOf 更宽松。

分层落地:在哪一层校验

同一份 schema 可以复用在多个位置,但职责不同:

  1. 网关 / 边缘:只做粗校验(体积、必填、显而易见的类型错误),尽早拒掉恶意流量;
  2. 服务入口:做完整的结构校验并返回字段级错误,这是唯一必须严格执行的一层;
  3. 数据库前:依赖约束(非空、唯一、长度)作为最后一道防线,防止绕过应用写入的脏数据;
  4. 前端:复用同一份 schema 做即时提示,提升体验但不作为安全边界。

版本化与兼容演进

  • 加字段:新增可选字段是兼容变更;若字段必填,先给默认值再逐步收紧;
  • 改类型 / 改名:属于破坏性变更,应新版本并行,并在旧版本返回弃用告警;
  • 共享 schema 要版本号:把 schema 当作接口契约的一部分随接口一起版本化,避免一处修改影响所有依赖方;
  • 用代码生成而不是手抄:由 schema 生成 TS 类型或校验代码,避免文档、类型与实现三处不一致。

常见误区

  • 把 schema 当业务规则引擎:跨字段逻辑与外部查询仍需代码;
  • 只校验请求不校验响应:上游返回的数据同样可能不符合预期,关键依赖应校验响应以防脏数据扩散;
  • 错误提示不定位:只返回“参数错误”会让调用方反复试错,应返回字段路径与原因列表。

常用关键字速查

  • 类型与范围:type、enum、minimum/maximum、minLength/maxLength;
  • 结构:properties、required、additionalProperties、items、minItems;
  • 组合:allOf 全部满足、anyOf 任一满足、oneOf 恰好一个满足、not 取反;
  • 条件:if/then/else 表达“当某字段存在时必须同时满足另一约束”;
  • 复用:$defs 配 $ref 定义公共片段,避免复制粘贴造成漂移。

性能注意点

大规模校验时注意两点:一是把编译后的校验器缓存起来,避免每次请求都重新编译 schema;二是避免过深的 oneOf 组合,它们可能导致组合爆炸。对超高吞吐路径,可先做轻量字段检查,仅在必要时才走完整校验。

与类型系统的配合

校验 schema 与静态类型是两件事:类型在编译期防止“写错”,schema 在运行期防止“收到错”。两者都应由同一份定义生成,否则会出现类型声明允许、运行时却拒绝的割裂。定期用真实流量样本跑一遍校验,能发现声明与现实的偏差。