OpenAPI 规范详解
掌握 OpenAPI 3.0 规范,实现高效的 API-First 开发
OpenAPI 规范概述
OpenAPI(原 Swagger)是一个用于描述 RESTful API 的规范,它使得人类和计算机都能够理解服务的功能,而无需访问源代码、文档或网络流量检查。
核心优势
1. 标准化的 API 描述
yaml
# 统一的、机器可读的 API 描述格式
openapi: 3.0.3
info:
title: 用户管理 API
version: 1.0.0
description: |
提供用户注册、认证、管理等功能的 RESTful API2. 自动化代码生成
3. API-First 工作流支持
- 设计优先: 在编码前完成 API 设计
- 并行开发: 前后端基于规范独立开发
- 契约测试: 确保实现符合规范
- 文档同步: 代码和文档保持一致
OpenAPI 3.0 结构详解
1. 基本结构
yaml
# 必需:OpenAPI 版本声明
openapi: 3.0.3
# 必需:API 元信息
info:
title: API 标题
version: 1.0.0
description: API 描述
termsOfService: https://example.com/terms
contact:
name: API 支持团队
email: api@example.com
url: https://support.example.com
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
# 可选:服务器配置
servers:
- url: https://api.example.com/v1
description: 生产环境
variables:
protocol:
enum: [http, https]
default: https
- url: https://staging-api.example.com/v1
description: 测试环境
# 可选:标签定义
tags:
- name: users
description: 用户管理相关接口
externalDocs:
description: 更多信息
url: https://docs.example.com/users
# 必需:API 路径定义
paths: {}
# 可选:组件定义(推荐使用)
components:
schemas: {}
parameters: {}
responses: {}
examples: {}
requestBodies: {}
headers: {}
securitySchemes: {}
links: {}
callbacks: {}
# 可选:安全配置
security:
- bearerAuth: []
# 可选:外部文档
externalDocs:
description: API 完整文档
url: https://docs.example.com2. 路径定义(Paths)
基本路径结构
yaml
paths:
/users:
# 路径级别的通用配置
summary: 用户资源端点
description: 管理系统用户的相关操作
# GET 操作
get:
summary: 获取用户列表
description: |
获取系统中所有用户的列表,支持分页、排序和过滤。
需要有效的认证令牌。
operationId: listUsers # 代码生成时的方法名
tags: [users]
# 请求参数
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/SizeParam'
- name: status
in: query
description: 用户状态过滤
required: false
schema:
type: string
enum: [active, inactive, suspended]
example: active
# 响应定义
responses:
'200':
description: 成功获取用户列表
content:
application/json:
schema:
$ref: '#/components/schemas/UserListResponse'
example:
data:
- id: "123"
name: "张三"
email: "zhangsan@example.com"
pagination:
page: 1
size: 20
total: 100
'401':
$ref: '#/components/responses/UnauthorizedError'
'500':
$ref: '#/components/responses/InternalServerError'
# 安全要求
security:
- bearerAuth: []
# POST 操作
post:
summary: 创建新用户
operationId: createUser
tags: [users]
requestBody:
description: 用户创建请求
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserRequest'
examples:
normalUser:
summary: 普通用户
value:
name: "李四"
email: "lisi@example.com"
password: "SecurePass123"
role: "user"
adminUser:
summary: 管理员用户
value:
name: "管理员"
email: "admin@example.com"
password: "AdminPass123"
role: "admin"
responses:
'201':
description: 用户创建成功
headers:
Location:
description: 新创建用户的 URI
schema:
type: string
example: "/users/123"
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
$ref: '#/components/responses/BadRequestError'
'409':
$ref: '#/components/responses/ConflictError'
/users/{userId}:
# 路径参数
parameters:
- name: userId
in: path
description: 用户唯一标识
required: true
schema:
type: string
format: uuid
example: "123e4567-e89b-12d3-a456-426614174000"
get:
summary: 获取用户详情
operationId: getUserById
tags: [users]
responses:
'200':
description: 成功获取用户信息
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
$ref: '#/components/responses/NotFoundError'
put:
summary: 更新用户信息
operationId: updateUser
tags: [users]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateUserRequest'
responses:
'200':
description: 更新成功
content:
application/json:
schema:
$ref: '#/components/schemas/User'
delete:
summary: 删除用户
operationId: deleteUser
tags: [users]
responses:
'204':
description: 删除成功
'404':
$ref: '#/components/responses/NotFoundError'3. 数据模型定义(Schemas)
基本类型定义
yaml
components:
schemas:
# 基础用户模型
User:
type: object
description: 用户信息
required: [id, name, email, createdAt]
properties:
id:
type: string
format: uuid
description: 用户唯一标识
readOnly: true # 只在响应中出现
example: "123e4567-e89b-12d3-a456-426614174000"
name:
type: string
description: 用户姓名
minLength: 2
maxLength: 50
pattern: '^[\u4e00-\u9fa5a-zA-Z\s]+$'
example: "张三"
email:
type: string
format: email
description: 电子邮箱
example: "zhangsan@example.com"
phone:
type: string
description: 手机号码
pattern: '^1[3-9]\d{9}$'
nullable: true # 可以为 null
example: "13800138000"
age:
type: integer
description: 年龄
minimum: 0
maximum: 150
example: 25
balance:
type: number
format: double
description: 账户余额
minimum: 0
example: 1000.50
status:
type: string
description: 用户状态
enum: [active, inactive, suspended]
default: active
roles:
type: array
description: 用户角色列表
items:
type: string
enum: [admin, user, guest]
minItems: 1
uniqueItems: true
example: ["user"]
profile:
$ref: '#/components/schemas/UserProfile'
createdAt:
type: string
format: date-time
description: 创建时间
readOnly: true
example: "2024-01-01T00:00:00Z"
updatedAt:
type: string
format: date-time
description: 更新时间
readOnly: true
example: "2024-01-02T00:00:00Z"
# 嵌套对象
UserProfile:
type: object
description: 用户详细资料
properties:
avatar:
type: string
format: uri
description: 头像 URL
example: "https://example.com/avatar.jpg"
bio:
type: string
description: 个人简介
maxLength: 500
example: "热爱编程的开发者"
address:
$ref: '#/components/schemas/Address'
preferences:
type: object
description: 用户偏好设置
additionalProperties:
type: string
example:
theme: "dark"
language: "zh-CN"
# 地址模型
Address:
type: object
properties:
street:
type: string
example: "中山路100号"
city:
type: string
example: "上海"
province:
type: string
example: "上海市"
country:
type: string
example: "中国"
postalCode:
type: string
pattern: '^\d{6}$'
example: "200000"请求/响应模型
yaml
components:
schemas:
# 创建用户请求
CreateUserRequest:
type: object
required: [name, email, password]
properties:
name:
type: string
minLength: 2
maxLength: 50
email:
type: string
format: email
password:
type: string
format: password
minLength: 8
maxLength: 128
pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).*$'
description: 必须包含大小写字母和数字
phone:
type: string
pattern: '^1[3-9]\d{9}$'
role:
type: string
enum: [admin, user]
default: user
# 更新用户请求
UpdateUserRequest:
type: object
properties:
name:
type: string
minLength: 2
maxLength: 50
phone:
type: string
pattern: '^1[3-9]\d{9}$'
profile:
$ref: '#/components/schemas/UserProfile'
minProperties: 1 # 至少提供一个字段
# 分页响应
UserListResponse:
type: object
required: [data, pagination]
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
$ref: '#/components/schemas/Pagination'
# 分页信息
Pagination:
type: object
required: [page, size, total, totalPages]
properties:
page:
type: integer
minimum: 1
description: 当前页码
size:
type: integer
minimum: 1
maximum: 100
description: 每页大小
total:
type: integer
minimum: 0
description: 总记录数
totalPages:
type: integer
minimum: 0
description: 总页数
hasNext:
type: boolean
description: 是否有下一页
hasPrevious:
type: boolean
description: 是否有上一页高级特性
yaml
components:
schemas:
# 使用 oneOf(互斥选择)
NotificationMethod:
oneOf:
- $ref: '#/components/schemas/EmailNotification'
- $ref: '#/components/schemas/SmsNotification'
- $ref: '#/components/schemas/PushNotification'
discriminator:
propertyName: type
mapping:
email: '#/components/schemas/EmailNotification'
sms: '#/components/schemas/SmsNotification'
push: '#/components/schemas/PushNotification'
# 使用 allOf(组合)
AdminUser:
allOf:
- $ref: '#/components/schemas/User'
- type: object
required: [adminLevel, permissions]
properties:
adminLevel:
type: integer
minimum: 1
maximum: 10
permissions:
type: array
items:
type: string
# 使用 anyOf(任意组合)
SearchFilter:
anyOf:
- type: object
properties:
name:
type: string
- type: object
properties:
email:
type: string
- type: object
properties:
phone:
type: string4. 参数定义(Parameters)
yaml
components:
parameters:
# 路径参数
UserIdParam:
name: userId
in: path
description: 用户唯一标识
required: true
schema:
type: string
format: uuid
example: "123e4567-e89b-12d3-a456-426614174000"
# 查询参数
PageParam:
name: page
in: query
description: 页码(从1开始)
required: false
schema:
type: integer
minimum: 1
default: 1
example: 1
SizeParam:
name: size
in: query
description: 每页大小
required: false
schema:
type: integer
minimum: 1
maximum: 100
default: 20
example: 20
# 请求头参数
ApiKeyHeader:
name: X-API-Key
in: header
description: API 密钥
required: true
schema:
type: string
example: "your-api-key-here"
# Cookie 参数
SessionIdCookie:
name: sessionId
in: cookie
description: 会话 ID
required: false
schema:
type: string5. 响应定义(Responses)
yaml
components:
responses:
# 成功响应
SuccessResponse:
description: 操作成功
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
message:
type: string
example: "操作成功"
# 错误响应
BadRequestError:
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
invalidEmail:
summary: 邮箱格式错误
value:
code: "INVALID_EMAIL"
message: "邮箱格式不正确"
timestamp: "2024-01-01T00:00:00Z"
missingField:
summary: 缺少必填字段
value:
code: "MISSING_FIELD"
message: "缺少必填字段:name"
details:
field: "name"
timestamp: "2024-01-01T00:00:00Z"
UnauthorizedError:
description: 未授权访问
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: "UNAUTHORIZED"
message: "请先登录"
timestamp: "2024-01-01T00:00:00Z"
headers:
WWW-Authenticate:
description: 认证方式
schema:
type: string
example: "Bearer"
NotFoundError:
description: 资源不存在
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: "NOT_FOUND"
message: "用户不存在"
timestamp: "2024-01-01T00:00:00Z"
ConflictError:
description: 资源冲突
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: "CONFLICT"
message: "邮箱已被注册"
timestamp: "2024-01-01T00:00:00Z"
InternalServerError:
description: 服务器内部错误
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
code: "INTERNAL_ERROR"
message: "服务器内部错误,请稍后重试"
timestamp: "2024-01-01T00:00:00Z"
traceId: "abc123def456"6. 安全定义(Security)
yaml
components:
securitySchemes:
# Bearer Token (JWT)
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: 使用 JWT 令牌进行认证
# API Key
apiKey:
type: apiKey
in: header
name: X-API-Key
description: API 密钥认证
# OAuth2
oauth2:
type: oauth2
description: OAuth2 认证
flows:
authorizationCode:
authorizationUrl: https://auth.example.com/oauth/authorize
tokenUrl: https://auth.example.com/oauth/token
refreshUrl: https://auth.example.com/oauth/refresh
scopes:
read:users: 读取用户信息
write:users: 修改用户信息
admin: 管理员权限
clientCredentials:
tokenUrl: https://auth.example.com/oauth/token
scopes:
api:access: API 访问权限
# Basic Auth
basicAuth:
type: http
scheme: basic
description: 基本认证(用户名/密码)
# Cookie Auth
cookieAuth:
type: apiKey
in: cookie
name: session_id
description: Cookie 会话认证7. 回调定义(Callbacks)
yaml
components:
callbacks:
# Webhook 回调
userCreatedWebhook:
'{$request.body#/callbackUrl}':
post:
summary: 用户创建事件通知
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
event:
type: string
enum: [user.created]
timestamp:
type: string
format: date-time
data:
$ref: '#/components/schemas/User'
responses:
'200':
description: 通知接收成功代码生成最佳实践
1. 为代码生成优化的设计
yaml
# 使用 operationId 指定方法名
paths:
/users:
get:
operationId: listUsers # 生成的方法名
x-codegen-request-name: ListUsersRequest # 自定义请求类名
x-codegen-response-name: ListUsersResponse # 自定义响应类名
# 使用 x-* 扩展属性
components:
schemas:
User:
type: object
x-class-name: UserDTO # 自定义生成的类名
x-implements: [Serializable, Cloneable] # Java 接口实现
properties:
id:
type: string
x-field-name: userId # 自定义字段名
x-getter: getUserIdentifier # 自定义 getter 方法名2. 验证规则映射
yaml
# OpenAPI 验证规则会映射到生成代码的验证注解
properties:
email:
type: string
format: email
# 生成 Java: @Email
# 生成 Python: validators.email()
age:
type: integer
minimum: 0
maximum: 150
# 生成 Java: @Min(0) @Max(150)
# 生成 Python: validators.integer(min=0, max=150)
name:
type: string
minLength: 2
maxLength: 50
pattern: '^[a-zA-Z\s]+$'
# 生成 Java: @Size(min=2, max=50) @Pattern(regexp="^[a-zA-Z\\s]+$")
# 生成 Python: validators.string(min_length=2, max_length=50, regex=r'^[a-zA-Z\s]+$')3. 枚举生成策略
yaml
# 字符串枚举
status:
type: string
enum: [active, inactive, suspended]
x-enum-varnames: [ACTIVE, INACTIVE, SUSPENDED] # 自定义枚举常量名
x-enum-descriptions: # 枚举描述
active: 活跃状态
inactive: 非活跃状态
suspended: 已暂停
# 数字枚举
level:
type: integer
enum: [1, 2, 3]
x-enum-names: [BASIC, PREMIUM, ENTERPRISE]文档生成配置
1. 增强文档可读性
yaml
info:
description: |
# 用户管理 API
本 API 提供完整的用户管理功能,包括:
* 用户注册和认证
* 用户信息管理
* 权限和角色管理
## 快速开始
1. 获取 API 密钥
2. 使用密钥进行认证
3. 调用相应的接口
## 认证方式
支持以下认证方式:
- Bearer Token (推荐)
- API Key
- Basic Auth
# 为每个操作添加详细说明
paths:
/users:
post:
summary: 创建新用户
description: |
创建一个新的用户账号。
### 业务规则
- 邮箱必须唯一
- 密码必须符合安全要求
- 首次创建的用户默认为普通用户角色
### 注意事项
- 创建成功后会发送欢迎邮件
- 用户需要通过邮件验证才能激活账号2. 示例数据
yaml
components:
examples:
# 定义可重用的示例
ValidUser:
summary: 有效的用户数据
value:
id: "123e4567-e89b-12d3-a456-426614174000"
name: "张三"
email: "zhangsan@example.com"
phone: "13800138000"
status: "active"
roles: ["user"]
createdAt: "2024-01-01T00:00:00Z"
UserList:
summary: 用户列表示例
value:
data:
- id: "123"
name: "张三"
email: "zhangsan@example.com"
- id: "456"
name: "李四"
email: "lisi@example.com"
pagination:
page: 1
size: 20
total: 2
totalPages: 1
hasNext: false
hasPrevious: false
# 在操作中引用示例
paths:
/users/{userId}:
get:
responses:
'200':
content:
application/json:
examples:
success:
$ref: '#/components/examples/ValidUser'测试集成
1. 契约测试支持
yaml
# 添加测试相关的元数据
paths:
/users:
post:
x-contract-test:
priority: high
scenarios:
- name: "成功创建用户"
given: "邮箱未被使用"
when: "提供有效的用户信息"
then: "返回201和用户信息"
- name: "邮箱已存在"
given: "邮箱已被注册"
when: "使用相同邮箱注册"
then: "返回409冲突错误"2. Mock 数据生成
yaml
# 使用 x-faker 扩展生成测试数据
components:
schemas:
User:
properties:
name:
type: string
x-faker: name.fullName
email:
type: string
format: email
x-faker: internet.email
phone:
type: string
x-faker: phone.phoneNumber
avatar:
type: string
format: uri
x-faker: image.avatar版本管理策略
1. URL 版本控制
yaml
servers:
- url: https://api.example.com/v1
description: API v1(当前版本)
- url: https://api.example.com/v2
description: API v2(开发中)
- url: https://api-legacy.example.com
description: 旧版 API(已废弃)2. 废弃标记
yaml
paths:
/users/search:
get:
deprecated: true # 标记为已废弃
x-deprecation-date: "2024-12-31"
x-replacement: "/users?q={query}"
summary: 搜索用户(已废弃)
description: |
**⚠️ 此接口已废弃,将于 2024-12-31 下线**
请使用新的搜索接口:`GET /users?q={query}`工具集成
1. VS Code 插件配置
json
// .vscode/settings.json
{
"openapi.validate": true,
"openapi.linting": {
"rules": {
"oas3-api-servers": "error",
"operation-operationId": "error",
"operation-summary": "warning",
"oas3-schema": "error"
}
}
}2. 预提交钩子
yaml
# .pre-commit-config.yaml
repos:
- repo: https://github.com/python-openapi/openapi-spec-validator
rev: v0.5.1
hooks:
- id: openapi-spec-validator
files: 'api/.*\.yaml$'3. CI/CD 集成
yaml
# GitHub Actions
name: OpenAPI Validation
on: [push, pull_request]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Validate OpenAPI Spec
run: |
npx @redocly/cli lint api/openapi.yaml
- name: Generate Code
run: |
npx @openapitools/openapi-generator-cli generate \
-i api/openapi.yaml \
-g spring \
-o generated/
- name: Run Contract Tests
run: |
npm run test:contract常见问题
Q: 如何处理循环引用?
yaml
# 使用 $ref 处理循环引用
components:
schemas:
Category:
type: object
properties:
id:
type: string
parent:
$ref: '#/components/schemas/Category' # 自引用
children:
type: array
items:
$ref: '#/components/schemas/Category'Q: 如何定义文件上传?
yaml
paths:
/users/{userId}/avatar:
post:
summary: 上传用户头像
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
properties:
file:
type: string
format: binary
description: 图片文件(支持 JPG、PNG)
encoding:
file:
contentType: image/png, image/jpegQ: 如何定义动态响应?
yaml
# 使用 oneOf 定义多种可能的响应
responses:
'200':
description: 成功响应
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/User'
- $ref: '#/components/schemas/UserList'
discriminator:
propertyName: responseType总结
OpenAPI 规范是实现 API-First 开发的基石。通过:
- 规范化设计: 统一 API 描述格式
- 自动化生成: 减少手工编码工作
- 文档同步: 保持代码和文档一致
- 测试集成: 支持契约测试和自动化测试
我们可以构建高质量、可维护的 API 系统。记住,好的 API 设计是成功的一半!