不喜欢广告? 无广告 今天

JSON模式验证 在发布前捕获API合约违规

发布日期

JSON模式验证在源头捕获API合约违规。学习核心关键词、Express中的AJV中间件、FastAPI集成、模式漂移检测以及与OpenAPI的关系。

JSON模式验证:在发布前捕获API合同违规行为 1
广告 移除?

如果您的API返回一个在模式中不存在的字段,或者请求体缺少必需属性,那么您就存在合同违规。这些错误很微妙,通常无声无息,而且在生产环境中很难发现。JSON Schema验证是在问题被引入时更早地捕获它们的层。

本指南介绍了JSON Schema的工作原理,以及在Node.js、Python和AWS中应使用哪些验证器,以及如何保持您的模式与现实保持一致。

什么是JSON Schema

JSON Schema是一种描述JSON文档结构的词汇。它不是代码,而是元数据。您编写一个模式,声明一个有效的JSON对象应该是什么样子,然后运行验证器进行验证。

该模式本身是一个JSON文档。一个最小示例:

{
  "$schema": "https://json-schema.org/draft/2020-12",
  "type": "object",
  "properties": {
    "email": { "type": "string", "format": "email" },
    "age":   { "type": "integer", "minimum": 0 }
  },
  "required": ["email"]
}

就这样。将此内容传递给任何JSON负载的验证器,您将得到通过或一个结构化的失败列表。

核心关键字

少数几个关键字可以覆盖大多数实际场景中的验证需求:

关键字 验证内容 例子
type 值的数据类型 "type": "string"
properties 对象字段的结构 "properties": { "name": { "type": "string" } }
required 哪些属性必须存在 "required": ["email", "password"]
additionalProperties 是否允许未知属性 "additionalProperties": false
enum 值必须是固定集合中的一个 "enum": ["admin", "editor", "viewer"]
format 语义格式检查 "format": "email""format": "date-time"
pattern 字符串必须匹配正则表达式 "pattern": "^[a-z0-9-]+$"
$ref 引用到另一个模式 "$ref": "#/$defs/Address"

additionalProperties: false 值得特别提及。它是使您的模式严格的关键字——任何未在 properties 声明的属性将触发验证错误。它默认是关闭的,这意味着大多数模式在没有启用的情况下会默默接受垃圾字段。

完整模式:用户注册请求

这里是一个完整的用于注册端点请求体的JSON Schema。这是您会编写一次,并在所有接触该端点的层中进行验证的模式类型。

{
  "$schema": "https://json-schema.org/draft/2020-12",
  "title": "UserRegistration",
  "type": "object",
  "required": ["email", "password", "username"],
  "additionalProperties": false,
  "properties": {
    "email": {
      "type": "string",
      "format": "email"
    },
    "password": {
      "type": "string",
      "minLength": 8
    },
    "username": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_]{3,32}$"
    },
    "role": {
      "type": "string",
      "enum": ["user", "admin"],
      "default": "user"
    },
    "birthDate": {
      "type": "string",
      "format": "date"
    }
  }
}

您可以使用 IO Tools JSON Schema Validator 来交互式测试任何负载,然后再将其集成到您的代码库中。

验证API请求体

Express + AJV

AJV 是Node.js中最快的JSON Schema验证器。以下是一个Express中间件,它在请求到达处理函数之前验证请求体:

import Ajv from "ajv";
import addFormats from "ajv-formats";

const ajv = new Ajv({ allErrors: true });
addFormats(ajv);

function validateBody(schema) {
  const validate = ajv.compile(schema);
  return (req, res, next) => {
    if (validate(req.body)) {
      return next();
    }
    res.status(400).json({
      error: "Validation failed",
      details: validate.errors,
    });
  };
}

// Usage
app.post("/register", validateBody(registrationSchema), registerHandler);

allErrors: true 告诉AJV收集文档中的所有错误,而不是在第一个错误处停止——当您希望一次性将所有验证失败返回给客户端时非常有用。

FastAPI(Python)

在Python中,FastAPI使用Pydantic作为底层,并自动从您的类型注解生成JSON Schema。如果您处理的是来自外部源的原始JSON Schema而不是Pydantic模型, jsonschema 是标准库:

from jsonschema import validate, ValidationError

schema = { ... }  # your schema dict

try:
    validate(instance=request_body, schema=schema)
except ValidationError as e:
    return {"error": e.message}, 400

AWS API Gateway

API Gateway原生支持请求体验证。您定义一个模型(即一个JSON Schema文档),并将其附加到您的方法作为 REQUEST_BODY 验证器。任何验证失败的请求将在网关级别被拒绝——在您的Lambda函数运行之前。这消除了整个类别的处理错误,并减少了无效流量的冷启动调用。

可重用的模式与 $ref$defs

当多个模式共享一个常见结构——例如地址、货币金额或分页对象时——请在 $defs 中定义一次,并通过 $ref:

{
  "$defs": {
    "Address": {
      "type": "object",
      "required": ["street", "city", "country"],
      "properties": {
        "street": { "type": "string" },
        "city":   { "type": "string" },
        "country": { "type": "string", "pattern": "^[A-Z]{2}$" }
      }
    }
  },
  "type": "object",
  "properties": {
    "billingAddress":  { "$ref": "#/$defs/Address" },
    "shippingAddress": { "$ref": "#/$defs/Address" }
  }
}

引用它。 $ref 对于大型项目,模式存储在单独的文件中,并通过 $id 指向一个URI。支持 addSchema().

模式漂移

当模式和实际API发生分歧时,就会出现模式漂移。这比您想象的更常见:模式只写一次,API不断演进,而没有人更新模式。

症状很微妙。代码中的字段被重命名,但模式中没有更新——验证仍然通过,因为 additionalProperties 没有设置为false。一个必需字段在实践中变为可选,因为代码不再检查它——模式仍然要求它是必需的,但直到客户端发送没有该字段的请求,没有人注意到。

要捕捉漂移,必须将模式验证视为测试,而不是运行时检查。一些团队通过与真实响应固定数据的快照测试来自动化此过程。其他人则在CI中运行模式验证器,以测试一组捕获的API请求。关键点是,模式必须定期使用,而不是只在部署时使用。

JSON Schema 与 OpenAPI

OpenAPI(以前称为Swagger)使用JSON Schema来描述请求和响应体,但有一些修改。早期版本(OpenAPI 2.0、3.0)使用了JSON Schema的子集并添加了扩展。OpenAPI 3.1更贴近于JSON Schema draft 2020-12,因此这些模式基本上是互操作的。

实际区别是:OpenAPI将模式封装在一个更大的文档中,该文档还描述了路径、操作、身份验证和服务器。而单独的JSON Schema只是一个验证合同。如果您正在构建一个以模式为中心的API,您可以为每个端点编写JSON Schema,进行验证,然后将这些模式直接提升到OpenAPI文档中。

从现有数据生成模式

手动编写模式对于新API来说效果很好。对于已有但未文档化的API,从真实负载生成模式通常更快,然后进行优化。

genson (Python) 和 generate-schema (Node.js) 可以从样本JSON对象生成一个草案模式。生成的模式通常过于宽松——所有内容都变为可选,没有 additionalProperties: false ——但它为您提供了一个起点。然后您添加 required,收紧 type 定义,以及在值有边界时添加 enum 约束。

这种方法在引入第三方API且没有模式文档时也非常有用。运行几个响应通过模式生成器,合并输出,您就有了一个可用的合同来验证。


JSON Schema验证应存在于每个接受结构化数据的层——HTTP中间件、消息队列消费者、数据库写入路径。合同违规越早被发现,修复成本就越低。一个 在线JSON Schema验证器 允许您在投入任何库集成之前,用真实负载来原型化模式,这使得它成为任何验证工作的第一步。

想要享受无广告的体验吗? 立即无广告

安装我们的扩展

将 IO 工具添加到您最喜欢的浏览器,以便即时访问和更快地搜索

添加 Chrome 扩展程序 添加 边缘延伸 添加 Firefox 扩展 添加 Opera 扩展

记分板已到达!

记分板 是一种有趣的跟踪您游戏的方式,所有数据都存储在您的浏览器中。更多功能即将推出!

广告 移除?
广告 移除?
广告 移除?

新闻角 包含技术亮点

参与其中

帮助我们继续提供有价值的免费工具

给我买杯咖啡
广告 移除?