OpenAPI规范详解

OpenAPI 规范完整指南 - 从基础到实战

#tech / dev / backend #type / concept #status / evergreen

[!info] related notes

OpenAPI 规范详解

OpenAPI = API 设计的单一事实来源 (Single Source of Truth)

🎯 什么是 OpenAPI?

OpenAPI Specification (OAS) 是一种 API 描述标准,用于定义 RESTful API 的结构、端点、参数、响应等。它是与语言无关的,使用 YAML 或 JSON 格式编写。

💡 前身: OpenAPI 原名 Swagger Specification,2016 年捐赠给 Linux 基金会后更名为 OpenAPI。

版本历史

  • Swagger 2.0 (2014) - 广泛使用的旧版本
  • OpenAPI 3.0 (2017) - 重大改进,简化结构
  • OpenAPI 3.1 (2021) - 完全兼容 JSON Schema

🏗️ 核心作用

┌─────────────────────────────────────────────────────────────────────────┐
│                    OpenAPI 在开发流程中的作用                            │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   ┌──────────────┐                           ┌──────────────┐          │
│   │   前端团队    │                           │   后端团队    │          │
│   └──────┬───────┘                           └──────┬───────┘          │
│          │                                          │                   │
│          │    ┌────────────────────────────┐       │                   │
│          │    │                            │       │                   │
│          └───►│   OpenAPI 规范文件          │◄──────┘                   │
│               │   (api-spec.yaml)          │                           │
│               │                            │                           │
│               │   📋 单一事实来源           │                           │
│               │   (Single Source of Truth) │                           │
│               └────────────┬───────────────┘                           │
│                            │                                            │
│          ┌─────────────────┼─────────────────┐                         │
│          ▼                 ▼                 ▼                         │
│   ┌────────────┐   ┌────────────┐   ┌────────────┐                    │
│   │ 生成文档    │   │ 生成客户端  │   │ Mock Server│                    │
│   │ (Swagger   │   │ SDK 代码    │   │ (前端先行  │                    │
│   │  UI)       │   │            │   │  开发)     │                    │
│   └────────────┘   └────────────┘   └────────────┘                    │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

OpenAPI 能做什么?

  1. 📚 自动生成 API 文档 - Swagger UI、ReDoc
  2. 🔧 生成客户端 SDK - TypeScript、Python、Java 等
  3. 🎭 Mock Server - 前端先行开发,无需等待后端
  4. ✅ API 验证 - 验证实际 API 是否符合规范
  5. 🛠️ 生成服务端骨架 - NestJS、Spring Boot 等

📝 OpenAPI 文件结构

基本结构

openapi: 3.0.3
info:
  title: My API
  version: 1.0.0
  description: API 描述
  contact:
    name: Team Name
    email: team@example.com

servers:
  - url: https://api.example.com/v1
    description: Production
  - url: http://localhost:3000/v1
    description: Development

tags:
  - name: users
    description: 用户管理
  - name: posts
    description: 文章管理

paths:
  /users:
    get:
      summary: 获取用户列表
      tags: [users]
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/User'

components:
  schemas:
    User:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
          description: 用户ID
        name:
          type: string
          description: 用户名
        email:
          type: string
          format: email
  
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

完整示例:同步 API

openapi: 3.0.3
info:
  title: DailyUse Sync API
  description: 多设备数据同步服务 API
  version: 1.0.0
  contact:
    name: DailyUse Team

servers:
  - url: https://api.dailyuse.app/v1
    description: Production
  - url: http://localhost:3000/v1
    description: Development

tags:
  - name: sync
    description: 同步操作
  - name: devices
    description: 设备管理
  - name: conflicts
    description: 冲突处理

paths:
  /sync/push:
    post:
      tags: [sync]
      summary: 推送本地变更
      description: 将客户端的变更推送到服务端
      operationId: pushChanges
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SyncPushRequest'
            examples:
              basic:
                summary: 基本示例
                value:
                  deviceId: "550e8400-e29b-41d4-a716-446655440000"
                  changes:
                    - eventId: "abc-123"
                      entityType: "goal"
                      entityId: "goal-001"
                      operation: "create"
                      payload:
                        title: "学习 OpenAPI"
                        deadline: "2025-12-31"
                      baseVersion: 0
                      clientTimestamp: 1702886400000
      responses:
        '200':
          description: 推送成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SyncPushResponse'
              examples:
                success:
                  value:
                    success: true
                    data:
                      accepted: ["abc-123"]
                      conflicts: []
                      newVersion: 1
        '409':
          description: 版本冲突
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ConflictError'
        '429':
          description: 速率限制
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: "rate_limit_exceeded"
                  retryAfter:
                    type: integer
                    description: 秒数
                    example: 60

  /sync/pull:
    get:
      tags: [sync]
      summary: 拉取服务端变更
      security:
        - bearerAuth: []
      parameters:
        - name: deviceId
          in: query
          required: true
          schema:
            type: string
            format: uuid
        - name: since
          in: query
          required: true
          schema:
            type: integer
            minimum: 0
          description: 客户端当前版本号
      responses:
        '200':
          description: 拉取成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SyncPullResponse'

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: 'JWT token,格式: `Bearer <token>`'

  schemas:
    # 请求体定义
    SyncPushRequest:
      type: object
      required:
        - deviceId
        - changes
      properties:
        deviceId:
          type: string
          format: uuid
          description: 设备唯一标识
          example: "550e8400-e29b-41d4-a716-446655440000"
        changes:
          type: array
          items:
            $ref: '#/components/schemas/SyncChange'
          minItems: 1
          maxItems: 100
          description: 变更列表(最多100条)
    
    # 变更对象
    SyncChange:
      type: object
      required:
        - eventId
        - entityType
        - entityId
        - operation
        - payload
        - baseVersion
      properties:
        eventId:
          type: string
          format: uuid
          description: 事件唯一标识
        entityType:
          type: string
          enum: [goal, task, reminder, schedule, habit]
          description: 实体类型
        entityId:
          type: string
          format: uuid
          description: 实体ID
        operation:
          type: string
          enum: [create, update, delete]
          description: 操作类型
        payload:
          type: object
          additionalProperties: true
          description: 实体数据(operation=delete时可为空)
        baseVersion:
          type: integer
          minimum: 0
          description: 客户端基于的版本号
        clientTimestamp:
          type: integer
          description: Unix 毫秒时间戳
          example: 1702886400000
    
    # 响应体定义
    SyncPushResponse:
      type: object
      properties:
        success:
          type: boolean
        data:
          type: object
          properties:
            accepted:
              type: array
              items:
                type: string
                format: uuid
              description: 已接受的事件ID列表
            conflicts:
              type: array
              items:
                $ref: '#/components/schemas/ConflictInfo'
              description: 冲突列表
            newVersion:
              type: integer
              description: 服务端当前最新版本
    
    SyncPullResponse:
      type: object
      properties:
        success:
          type: boolean
        data:
          type: object
          properties:
            changes:
              type: array
              items:
                $ref: '#/components/schemas/SyncChange'
            currentVersion:
              type: integer
              description: 服务端当前版本
            hasMore:
              type: boolean
              description: 是否还有更多变更
    
    # 冲突信息
    ConflictInfo:
      type: object
      properties:
        eventId:
          type: string
          format: uuid
        reason:
          type: string
          enum: [version_mismatch, entity_deleted, concurrent_update]
          description: 冲突原因
        serverVersion:
          type: integer
          description: 服务端版本号
        serverData:
          type: object
          additionalProperties: true
          description: 服务端最新数据
    
    # 错误响应
    ConflictError:
      type: object
      properties:
        error:
          type: string
          enum: [conflict]
        message:
          type: string
          example: "Version conflict detected"
        conflicts:
          type: array
          items:
            $ref: '#/components/schemas/ConflictInfo'

🛠️ 工具链使用

1. Swagger UI - 交互式文档

# 在线预览
npx @redocly/cli preview-docs api-spec.yaml

# 访问 http://localhost:8080

特性

  • 📖 自动生成美观的 API 文档
  • 🧪 在线测试 API 端点
  • 📋 自动生成 cURL 命令
  • 🔐 支持认证(Bearer Token、API Key)

2. OpenAPI Generator - 生成代码

# 安装
npm install -g @openapitools/openapi-generator-cli

# 生成 TypeScript Axios 客户端
openapi-generator-cli generate \
  -i api-spec.yaml \
  -g typescript-axios \
  -o ./packages/api-client

# 生成 Python FastAPI 服务端
openapi-generator-cli generate \
  -i api-spec.yaml \
  -g python-fastapi \
  -o ./backend

# 查看所有支持的生成器
openapi-generator-cli list

生成的客户端代码示例

// 自动生成的类型安全客户端
import { SyncApi, Configuration } from './api-client';

const config = new Configuration({
  basePath: 'https://api.dailyuse.app/v1',
  accessToken: 'your-jwt-token'
});

const api = new SyncApi(config);

// 完全类型安全!IDE 自动补全
const response = await api.pushChanges({
  deviceId: '550e8400-e29b-41d4-a716-446655440000',
  changes: [{
    eventId: 'abc-123',
    entityType: 'goal', // 类型提示:只能是 'goal' | 'task' | ...
    entityId: 'goal-001',
    operation: 'create',
    payload: { title: '学习 OpenAPI' },
    baseVersion: 0
  }]
});

// TypeScript 知道 response 的类型
console.log(response.data.newVersion); // number

3. Prism Mock Server - 前端先行开发

# 安装
npm install -g @stoplight/prism-cli

# 启动 Mock Server
prism mock api-spec.yaml
# 运行在 http://localhost:4010

# 验证 API 是否符合规范(代理模式)
prism proxy api-spec.yaml http://localhost:3000

前端立即开始开发

// 前端代码
const response = await fetch('http://localhost:4010/sync/push', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer fake-token'
  },
  body: JSON.stringify({
    deviceId: '550e8400-e29b-41d4-a716-446655440000',
    changes: [/* ... */]
  })
});

// Mock Server 会返回符合规范的模拟数据
const data = await response.json();
console.log(data); // { success: true, data: { accepted: [...], ... } }

4. Redocly - 专业文档

# 安装
npm install -g @redocly/cli

# 生成静态 HTML 文档
redocly build-docs api-spec.yaml -o docs/api.html

# 验证规范
redocly lint api-spec.yaml

📊 在 DailyUse 中的应用场景

Week 0: 契约优先开发流程

┌─────────────────────────────────────────────────────────────────────────┐
│                    契约优先开发流程                                       │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   Day 1-2: 定义 OpenAPI 规范                                            │
│   ┌─────────────────────────────────────────────────────────────────┐   │
│   │  api-spec.yaml                                                   │   │
│   │  ├── /sync/push   - 推送变更                                     │   │
│   │  ├── /sync/pull   - 拉取变更                                     │   │
│   │  ├── /devices     - 设备管理                                     │   │
│   │  └── /conflicts   - 冲突处理                                     │   │
│   └─────────────────────────────────────────────────────────────────┘   │
│                            │                                            │
│                            ▼                                            │
│   Day 3: 生成工具链                                                      │
│   ┌─────────────────────────────────────────────────────────────────┐   │
│   │                                                                  │   │
│   │   ┌────────────┐  ┌────────────┐  ┌────────────┐               │   │
│   │   │ Mock Server│  │ TS Client  │  │ API Docs   │               │   │
│   │   │ (Prism)    │  │ SDK        │  │ (Swagger)  │               │   │
│   │   └─────┬──────┘  └─────┬──────┘  └────────────┘               │   │
│   │         │               │                                       │   │
│   └─────────┼───────────────┼───────────────────────────────────────┘   │
│             │               │                                           │
│             ▼               ▼                                           │
│   Week 1+: 并行开发                                                      │
│   ┌─────────────────┐  ┌─────────────────┐                             │
│   │  前端开发        │  │  后端开发       │                             │
│   │                 │  │                 │                             │
│   │  使用 Mock      │  │  实现真实 API   │                             │
│   │  + TS Client    │  │  按契约开发     │                             │
│   │                 │  │                 │                             │
│   │  • 界面开发     │  │  • 业务逻辑     │                             │
│   │  • 状态管理     │  │  • 数据库操作   │                             │
│   │  • 错误处理     │  │  • 认证授权     │                             │
│   └─────────────────┘  └─────────────────┘                             │
│                                                                         │
│   Week 2+: 集成测试                                                      │
│   ┌─────────────────────────────────────────────────────────────────┐   │
│   │  用 Prism 代理模式验证 API 是否符合规范                           │   │
│   │  prism proxy api-spec.yaml http://localhost:3000                │   │
│   └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

优势分析

传统流程

后端开发 API → 前端等待 → 联调 → 发现接口不符合预期 → 重新开发

契约优先流程

定义 OpenAPI 规范 → 前后端并行开发 → 直接集成 → 自动验证

节省时间

  • ✅ 前端不再等待后端 API
  • ✅ 减少 80% 的接口沟通成本
  • ✅ 自动生成客户端代码,避免手写错误
  • ✅ API 文档永远是最新的

📊 OpenAPI vs 其他方案

特性OpenAPIGraphQLgRPC
协议REST (HTTP)HTTPHTTP/2
描述格式YAML/JSONSDLProto
类型安全✅ 可生成✅ 原生✅ 原生
浏览器支持✅ 原生✅ 原生⚠️ 需要 grpc-web
工具生态⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
学习曲线
适用场景通用 REST API复杂查询微服务通信
Mock Server✅ Prism✅ GraphQL Faker⚠️ 需手动
代码生成✅ 多语言✅ 多语言✅ 多语言

为什么选择 OpenAPI?

✅ 适合 DailyUse 的原因

  • 与现有 Express/NestJS API 无缝集成
  • 工具链成熟 (Swagger, Prism, Generators)
  • 前后端团队容易理解
  • 支持渐进式采用(可以先给部分 API 加规范)
  • 不需要改变现有技术栈

❌ 不适合的场景

  • 需要复杂嵌套查询(GraphQL 更好)
  • 微服务间高性能通信(gRPC 更好)
  • 实时双向流式通信(WebSocket 更好)

🎯 最佳实践

1. 文件组织

project/
├── docs/
│   ├── openapi/
│   │   ├── api-spec.yaml          # 主规范文件
│   │   ├── components/
│   │   │   ├── schemas/           # 数据模型
│   │   │   │   ├── user.yaml
│   │   │   │   └── sync.yaml
│   │   │   ├── responses/         # 响应定义
│   │   │   │   └── errors.yaml
│   │   │   └── parameters/        # 参数定义
│   │   │       └── pagination.yaml
│   │   └── paths/                 # API 端点
│   │       ├── users.yaml
│   │       └── sync.yaml
│   └── api.html                   # 生成的文档
└── packages/
    └── api-client/                # 生成的客户端 SDK

2. 使用 $ref 引用

# api-spec.yaml
paths:
  /users:
    $ref: './paths/users.yaml#/users'

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

3. 版本管理

info:
  version: 1.0.0
  
servers:
  - url: https://api.example.com/v1  # URL 中包含版本

4. 完整的错误定义

components:
  responses:
    BadRequest:
      description: 请求参数错误
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
                example: "validation_error"
              message:
                type: string
              details:
                type: object
                additionalProperties: true

5. 使用 examples

requestBody:
  content:
    application/json:
      schema:
        $ref: '#/components/schemas/User'
      examples:
        john:
          summary: John Doe
          value:
            name: "John Doe"
            email: "john@example.com"
        jane:
          summary: Jane Smith
          value:
            name: "Jane Smith"
            email: "jane@example.com"

🔗 相关资源

官方文档

工具推荐

相关知识


💡 常见问题

Q1: OpenAPI 3.0 vs 2.0 有什么区别?

主要改进

  • requestBody 替代 body 参数
  • components 统一组织可复用组件
  • 支持 oneOf/anyOf/allOf 复杂类型
  • 更好的示例支持
  • Callback 和 Link 支持

Q2: 如何处理大型 API 规范?

解决方案

  1. 使用 $ref 拆分文件
  2. 按模块组织(users/posts/sync)
  3. 使用工具合并(如 swagger-cli)

Q3: Mock Server 生成的数据太简单?

改进方法

schema:
  type: object
  properties:
    name:
      type: string
      example: "John Doe"  # 提供真实示例
    email:
      type: string
      format: email
      example: "john@example.com"

Q4: 如何在 CI/CD 中使用?

# .github/workflows/api.yml
name: API Validation
on: [push]
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Validate OpenAPI
        run: |
          npm install -g @redocly/cli
          redocly lint docs/api-spec.yaml
      - name: Generate Client
        run: |
          npm install -g @openapitools/openapi-generator-cli
          openapi-generator-cli generate -i docs/api-spec.yaml -g typescript-axios -o packages/api-client

📚 下一步

  1. 学习 REST 设计: restful-api
  2. 实战集成: openapi-swagger
  3. API 测试: nodejs-backend-api-integration-testing
创建于 2025/12/8 更新于 2026/5/27