Skip to content

03 · OpenAPI 文件结构:四大块洋葱

生活类比:一份完整的 openapi.yaml 就像一份正经的菜单—— 封面(餐厅名 + 地址)= info + servers, 菜品列表(菜名 + 价格 + 图)= paths, 通用配料表(番茄酱、薯条用在多个菜里)= components, 会员卡使用须知(支付方式)= security


1. 一份最小可运行的 yaml

yaml
openapi: 3.0.3                          # ① 用的哪一版规范
info:                                   # ② API 基本信息
  title: 小餐厅 API
  version: 1.0.0
servers:                                # ③ 服务部署在哪
  - url: https://api.example.com/v1
paths:                                  # ④ 接口列表
  /menu:
    get:
      responses:
        '200':
          description: OK

必须有的字段只有 3 个openapiinfo(含 title + version)、paths。其他都是可选。


2. 四层洋葱结构

openapi: 3.0.3

├── info                ← 元信息(标题、版本、联系人、协议)
├── servers             ← 服务器列表(多环境)
├── tags                ← 接口分组(用户、订单、商品...)

├── paths               ← 主菜:所有接口
│    └── /users
│         └── get
│              ├── parameters       ← 入参
│              ├── requestBody      ← 请求体
│              ├── responses        ← 响应
│              └── security         ← 鉴权

├── components          ← 复用区
│    ├── schemas             ← 数据模型(User、Product…)
│    ├── parameters          ← 通用参数(pageQuery、sortQuery…)
│    ├── requestBodies       ← 通用请求体
│    ├── responses           ← 通用响应
│    ├── headers             ← 通用响应头
│    ├── securitySchemes     ← 鉴权方案
│    └── examples            ← 示例数据

├── security            ← 全局鉴权(所有接口默认)
├── externalDocs        ← 外链文档
└── webhooks            ← 3.1+ 才有,第三方回调

老的 OpenAPI 2 (Swagger 2) 用的是 definitions / parameters / responses 平铺在顶层,3.0+ 全部统一塞进 components 里——结构清爽多了


3. 一个一个字段拆开讲

3.1 openapi · 版本号

yaml
openapi: 3.0.3      # 推荐:3.0 稳定生态最广
# 或
openapi: 3.1.0      # 最新:完全兼容 JSON Schema 2020-12

3.2 info · 元信息

yaml
info:
  title: 小餐厅 API           # 必填
  version: 1.0.0             # 必填,建议语义化版本(major.minor.patch)
  description: |
    多行描述,支持 **Markdown**。
    可以放些链接、注意事项。
  termsOfService: https://example.com/terms
  contact:
    name: 张三
    email: zhangsan@example.com
    url: https://example.com/support
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT

在 Swagger UI 顶部那个"API 标题 + 描述"块就是从这儿来的。

3.3 servers · 多环境部署

yaml
servers:
  - url: https://api.example.com/v1
    description: 生产环境
  - url: https://staging.api.example.com/v1
    description: 测试环境
  - url: http://localhost:3000/v1
    description: 本地

  # 可以用变量
  - url: https://{region}.api.example.com/{version}
    variables:
      region:
        default: cn-east
        enum: [cn-east, us-west, eu-central]
      version:
        default: v1

Swagger UI 顶部会出现一个下拉框,让你选环境。Try it out 时按当前选中的 URL 发请求。

3.4 tags · 接口分组

yaml
tags:
  - name: 用户
    description: 用户注册、登录、修改资料
    externalDocs:
      url: https://wiki.example.com/user
  - name: 订单
    description: 下单、支付、退款
  - name: 商品

然后在每个接口里:

yaml
paths:
  /users:
    get:
      tags: [用户]      # 这个接口归属于"用户"分组
      ...

UI 里就会按 tag 折叠分组——接口一多,强烈推荐用 tags

3.5 paths · 接口列表(核心,第 4 章详讲)

yaml
paths:
  /users:                       # 路径
    get: { ... }                # GET /users
    post: { ... }               # POST /users
  /users/{id}:                  # 带路径参数
    get: { ... }
    put: { ... }
    delete: { ... }

3.6 components · 复用区(核心,第 6 章详讲)

yaml
components:
  schemas:
    User:                       # 定义一个 User 数据模型
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
      required: [id, name]

  parameters:
    PageQuery:                  # 定义一个通用参数
      name: page
      in: query
      schema:
        type: integer
        default: 1

  responses:
    NotFound:                   # 通用 404 响应
      description: 资源不存在
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'

  securitySchemes:
    bearerAuth:                 # 鉴权方案
      type: http
      scheme: bearer

引用方式($ref):

yaml
paths:
  /users:
    get:
      parameters:
        - $ref: '#/components/parameters/PageQuery'
      responses:
        '404':
          $ref: '#/components/responses/NotFound'

$ref 是 OpenAPI 的"复用引擎"——下一节细讲。

3.7 security · 全局鉴权

yaml
# 全局:所有接口默认要 bearer 鉴权
security:
  - bearerAuth: []

paths:
  /login:
    post:
      security: []          # 单个接口可以覆盖:登录接口免鉴权

4. $ref 引用机制——Swagger 的"灵魂"

4.1 为什么要 $ref

不复用:

yaml
# 错示范:每个接口里把 User 的字段抄一遍
paths:
  /users/1:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: integer }
                  name: { type: string }
                  email: { type: string }
                  ... (重复 50 行)

  /users/2:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: integer }
                  name: { type: string }
                  email: { type: string }
                  ... (又抄一遍 😱)

复用:

yaml
components:
  schemas:
    User:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
        email: { type: string }

paths:
  /users/1:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'    # ✅ 一行搞定

4.2 $ref 的语法

yaml
# 当前文件内引用(最常见)
$ref: '#/components/schemas/User'

# 同目录其他文件
$ref: './schemas/user.yaml'
$ref: './schemas/user.yaml#/User'

# 远程 URL(不推荐)
$ref: 'https://example.com/schemas/user.yaml'

路径里的 # 是 JSON Pointer 语法,/components/schemas/User 表示按 key 一层层往下找。

4.3 常见错误

yaml
# ✗ 错:少了 #
$ref: 'components/schemas/User'

# ✗ 错:拼写
$ref: '#/components/schema/User'      # schemas 漏了 s

# ✗ 错:循环引用(A ref B、B ref A)
# 工具会无限展开,要么报错要么死循环

# ✗ 错:把 $ref 和别的字段并列
schema:
  $ref: '#/components/schemas/User'
  description: "用户对象"           # 会被忽略!$ref 必须独占

5. YAML vs JSON:选哪个?

OpenAPI 文件可以是 yaml 或 json,等价

yaml
# YAML 写法
openapi: 3.0.3
info:
  title: My API
  version: 1.0.0
json
// JSON 写法
{
  "openapi": "3.0.3",
  "info": {
    "title": "My API",
    "version": "1.0.0"
  }
}
维度YAMLJSON
可读性⭐⭐⭐⭐⭐ (无括号、无引号)⭐⭐⭐
注释✅ 支持 #❌ 不支持
体积小 30%
工具兼容全支持全支持
容易出错缩进错容易踩坑大括号配对易出错

手写选 yaml,机器生成(如后端框架自动导出)通常是 json。两者可以用 yq / js-yaml 互转。


6. 一份带所有顶层字段的"完整骨架"

复制这份,按需删减:

yaml
openapi: 3.0.3

info:
  title: 你的 API 名字
  version: 1.0.0
  description: |
    详细描述。
    支持 Markdown:**加粗**、[链接](https://example.com)。
  contact:
    name: 张三
    email: zhangsan@example.com
  license:
    name: MIT

servers:
  - url: https://api.example.com/v1
    description: 生产
  - url: http://localhost:3000/v1
    description: 本地

tags:
  - name: user
    description: 用户管理
  - name: order
    description: 订单管理

security:
  - bearerAuth: []         # 全局默认 token 鉴权

paths:
  /ping:
    get:
      tags: [system]
      summary: 健康检查
      security: []          # 这个接口免鉴权
      responses:
        '200':
          description: pong
          content:
            text/plain:
              schema:
                type: string
                example: pong

components:
  schemas:
    User:
      type: object
      properties:
        id: { type: integer }
        name: { type: string }
      required: [id, name]
    Error:
      type: object
      properties:
        code: { type: integer }
        message: { type: string }

  parameters:
    PageQuery:
      name: page
      in: query
      schema: { type: integer, default: 1 }

  responses:
    NotFound:
      description: 资源不存在
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }

  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

externalDocs:
  description: 完整 Wiki
  url: https://wiki.example.com/api

这就是后续所有章节都会基于的"骨架"。背下来,写得快。


7. 多文件拆分 · 项目大了怎么办?

接口一多,单文件 5000 行没法维护。按模块拆

api/
├── openapi.yaml              ← 主文件,只放顶层 + ref
├── paths/
│   ├── users.yaml
│   ├── orders.yaml
│   └── products.yaml
└── schemas/
    ├── user.yaml
    ├── order.yaml
    └── common.yaml

主文件这样写:

yaml
# openapi.yaml
openapi: 3.0.3
info:
  title: 大型 API
  version: 1.0.0

paths:
  /users:
    $ref: './paths/users.yaml#/Users'
  /orders:
    $ref: './paths/orders.yaml#/Orders'

components:
  schemas:
    User:
      $ref: './schemas/user.yaml#/User'

redocly bundle / swagger-cli bundle 命令可以把多文件合并成一份发布文件。


8. 章末面试题速览

  1. OpenAPI 的根字段必填的有哪几个?openapiinfo(含 titleversion)、paths
  2. $refallOf 的区别?$ref 是引用一个 Schema 整体;allOf 是把多个 Schema 合并(继承)。
  3. 多环境部署怎么在文档里体现?servers 数组里写多个 URL,UI 顶部下拉切换。

🎬 可视化演示

下方 demo 用一个可折叠的洋葱图让你点进每一层,看清 info / servers / paths / components 各自是什么。

→ 打开 03_openapi_spec/demo.html

🎬 可视化演示

演示加载缓慢或样式异常?点此在新标签页打开 ↗