API 设计规范
本规范基于 API-First 设计理念,确保团队构建高质量、一致性的 API。
🎯 设计理念
API-First 原则
API-First 意味着在编写任何代码之前,先设计和定义 API:
核心优势:
- 🤝 并行开发:前后端可以同时开始开发
- 📋 明确契约:API 规范成为开发契约
- 🔄 快速反馈:通过 Mock 服务快速验证设计
- 📚 文档先行:API 文档与代码保持同步
- 🧪 测试驱动:基于规范编写测试用例
TDD (测试驱动开发) 集成
API-First + TDD 的开发流程:
- 设计阶段:编写 OpenAPI 规范
- 测试阶段:基于规范编写测试用例
- 开发阶段:编写代码使测试通过
- 重构阶段:优化代码质量
📋 API 设计标准
1. RESTful 设计原则
资源命名
yaml
# ✅ 推荐:使用复数名词
/api/v1/users
/api/v1/orders
/api/v1/products
# ❌ 不推荐:动词或单数
/api/v1/getUser
/api/v1/user
/api/v1/createOrderHTTP 方法使用
| 方法 | 用途 | 示例 | 幂等性 |
|---|---|---|---|
| GET | 获取资源 | GET /users/{id} | ✅ |
| POST | 创建资源 | POST /users | ❌ |
| PUT | 更新/替换资源 | PUT /users/{id} | ✅ |
| PATCH | 部分更新资源 | PATCH /users/{id} | ❌ |
| DELETE | 删除资源 | DELETE /users/{id} | ✅ |
状态码规范
yaml
# 成功响应
200: OK - 成功获取资源
201: Created - 成功创建资源
202: Accepted - 请求已接受,正在处理
204: No Content - 成功但无返回内容
# 客户端错误
400: Bad Request - 请求参数错误
401: Unauthorized - 未认证
403: Forbidden - 无权限
404: Not Found - 资源不存在
409: Conflict - 资源冲突
422: Unprocessable Entity - 参数验证失败
# 服务端错误
500: Internal Server Error - 服务器内部错误
502: Bad Gateway - 网关错误
503: Service Unavailable - 服务不可用2. OpenAPI 规范要求
必需字段
每个 API 规范必须包含:
yaml
openapi: 3.0.1
info:
title: API 名称 # 必需
description: API 详细描述 # 必需
version: 1.0.0 # 必需
contact: # 推荐
name: 开发团队
email: team@company.com
license: # 推荐
name: MIT
url: https://opensource.org/licenses/MIT
servers: # 必需
- url: https://api.example.com/v1
description: 生产环境
- url: https://staging-api.example.com/v1
description: 测试环境
tags: # 推荐
- name: users
description: 用户管理相关接口
paths: {} # 必需
components: # 推荐
schemas: {}
responses: {}
securitySchemes: {}接口文档要求
每个接口必须包含:
yaml
/api/v1/users/{id}:
get:
summary: 获取用户信息 # 必需:简短描述
description: | # 推荐:详细说明
根据用户ID获取用户的详细信息,包括基本资料、
权限信息等。需要有效的访问令牌。
operationId: getUser # 必需:唯一操作ID
tags: [users] # 必需:接口分组
parameters: # 必需:参数说明
- name: id
in: path
required: true
schema:
type: string
format: uuid
description: 用户的唯一标识符
example: "123e4567-e89b-12d3-a456-426614174000"
responses: # 必需:响应说明
'200':
description: 成功获取用户信息
content:
application/json:
schema:
$ref: '#/components/schemas/User'
example: # 推荐:响应示例
id: "123e4567-e89b-12d3-a456-426614174000"
name: "张三"
email: "zhangsan@example.com"
'404':
$ref: '#/components/responses/NotFound'
security: # 必需:安全要求
- bearerAuth: []3. 数据模型设计
Schema 设计原则
yaml
components:
schemas:
User:
type: object
required: [id, name, email, createdAt] # 明确必需字段
properties:
id:
type: string
format: uuid
description: 用户唯一标识符
example: "123e4567-e89b-12d3-a456-426614174000"
readOnly: true # 只读字段
name:
type: string
description: 用户姓名
minLength: 1 # 验证规则
maxLength: 50
example: "张三"
email:
type: string
format: email # 格式验证
description: 用户邮箱
example: "zhangsan@example.com"
phone:
type: string
pattern: '^1[3-9]\d{9}$' # 正则验证
description: 手机号码
example: "13800138000"
role:
type: string
enum: [admin, user, guest] # 枚举值
description: 用户角色
default: user
createdAt:
type: string
format: date-time
description: 创建时间
example: "2024-01-01T00:00:00Z"
readOnly: true数据类型规范
| 数据类型 | OpenAPI 类型 | 格式 | 示例 |
|---|---|---|---|
| 唯一ID | string | uuid | 123e4567-e89b-12d3-a456-426614174000 |
| 时间戳 | string | date-time | 2024-01-01T00:00:00Z |
| 日期 | string | date | 2024-01-01 |
| 邮箱 | string | user@example.com | |
| URL | string | uri | https://example.com |
| 手机号 | string | pattern | 13800138000 |
| 金额 | number | - | 99.99 (分为单位存储) |
4. 错误处理设计
统一错误响应格式
yaml
components:
schemas:
Error:
type: object
required: [code, message, timestamp]
properties:
code:
type: string
description: 业务错误代码
example: "USER_NOT_FOUND"
message:
type: string
description: 用户友好的错误信息
example: "用户不存在"
details:
type: object
description: 错误详细信息
example:
field: "userId"
value: "invalid-uuid"
timestamp:
type: string
format: date-time
description: 错误发生时间
example: "2024-01-01T00:00:00Z"
traceId:
type: string
description: 请求追踪ID
example: "abc123def456"错误代码规范
yaml
# 业务错误代码命名规范:[模块]_[动作]_[状态]
USER_NOT_FOUND # 用户不存在
USER_ALREADY_EXISTS # 用户已存在
ORDER_INVALID_STATUS # 订单状态无效
PAYMENT_INSUFFICIENT # 余额不足5. 分页和过滤设计
分页参数标准
yaml
parameters:
- name: page
in: query
description: 页码,从1开始
schema:
type: integer
minimum: 1
default: 1
- name: size
in: query
description: 每页大小
schema:
type: integer
minimum: 1
maximum: 100
default: 20
- name: sort
in: query
description: 排序字段,格式:field,direction
schema:
type: string
example: "createdAt,desc"分页响应格式
yaml
responses:
'200':
description: 成功获取列表
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
pagination:
type: object
properties:
page:
type: integer
description: 当前页码
size:
type: integer
description: 每页大小
total:
type: integer
description: 总记录数
totalPages:
type: integer
description: 总页数
hasNext:
type: boolean
description: 是否有下一页
hasPrevious:
type: boolean
description: 是否有上一页🔄 API-First 开发流程
1. 设计阶段(Design First)
1.1 需求分析
markdown
# API 设计检查清单
- [ ] 明确业务需求和用户场景
- [ ] 确定数据模型和关系
- [ ] 定义权限和安全要求
- [ ] 考虑性能和扩展性需求1.2 API 设计
bash
# 创建 API 规范文件
mkdir -p api/v1
touch api/v1/openapi.yaml
# 使用设计工具
# 推荐:Insomnia Designer, Swagger Editor, Stoplight Studio1.3 规范评审
bash
# 验证规范格式
npx @redocly/cli lint api/v1/openapi.yaml
# 团队评审检查点
- [ ] 遵循 RESTful 设计原则
- [ ] 命名规范一致
- [ ] 错误处理完整
- [ ] 文档清晰易懂
- [ ] 安全机制合理2. 实现阶段(Implementation)
2.1 生成 Mock 服务
bash
# 使用 Prism 生成 Mock 服务
npx @stoplight/prism-cli mock api/v1/openapi.yaml --port 3001
# 前端可以立即基于 Mock 数据开发
curl http://localhost:3001/api/v1/users2.2 生成测试框架
bash
# 生成客户端 SDK 用于测试
npx @openapitools/openapi-generator-cli generate \
-i api/v1/openapi.yaml \
-g typescript-fetch \
-o tests/generated/client
# 生成服务端存根
npx @openapitools/openapi-generator-cli generate \
-i api/v1/openapi.yaml \
-g spring \
-o src/generated/server \
--additional-properties=interfaceOnly=true2.3 TDD 开发循环
Step 1: 编写失败的测试
java
// Java 示例
@Test
@DisplayName("应该成功获取存在的用户")
void shouldReturnUserWhenUserExists() {
// Given
UUID userId = UUID.randomUUID();
// When
ResponseEntity<UserDto> response = userController.getUser(userId);
// Then
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody().getId()).isEqualTo(userId);
}python
# Python 示例
@pytest.mark.asyncio
async def test_should_return_user_when_exists():
"""应该成功获取存在的用户"""
# Given
user_id = uuid4()
# When
response = await client.get(f"/api/v1/users/{user_id}")
# Then
assert response.status_code == 200
assert response.json()["id"] == str(user_id)typescript
// TypeScript 示例
describe('UsersController', () => {
it('应该成功获取存在的用户', async () => {
// Given
const userId = '123e4567-e89b-12d3-a456-426614174000';
// When
const response = await request(app)
.get(`/api/v1/users/${userId}`)
.expect(200);
// Then
expect(response.body.id).toBe(userId);
});
});Step 2: 编写最小代码使测试通过
java
@RestController
public class UserController implements UsersApi {
@Override
public ResponseEntity<UserDto> getUser(UUID userId) {
// 最小实现
UserDto user = UserDto.builder()
.id(userId)
.name("测试用户")
.email("test@example.com")
.build();
return ResponseEntity.ok(user);
}
}Step 3: 重构优化
java
@RestController
@RequiredArgsConstructor
public class UserController implements UsersApi {
private final UserService userService;
@Override
public ResponseEntity<UserDto> getUser(UUID userId) {
UserDto user = userService.findById(userId);
return ResponseEntity.ok(user);
}
}3. 验证阶段(Validation)
3.1 契约测试
javascript
// 使用 Pact 进行契约测试
const { Pact } = require('@pact-foundation/pact');
describe('User API Contract', () => {
const provider = new Pact({
consumer: 'Frontend',
provider: 'User API',
port: 1234,
});
it('should get user by id', async () => {
await provider
.given('user exists')
.uponReceiving('a request for user')
.withRequest({
method: 'GET',
path: '/api/v1/users/123',
headers: {
'Accept': 'application/json',
},
})
.willRespondWith({
status: 200,
headers: {
'Content-Type': 'application/json',
},
body: {
id: '123',
name: 'John Doe',
email: 'john@example.com',
},
});
const response = await fetch('http://localhost:1234/api/v1/users/123');
expect(response.status).toBe(200);
});
});3.2 规范合规性测试
bash
# 使用 Dredd 进行 API 规范测试
npm install -g dredd
# 测试实现是否符合 OpenAPI 规范
dredd api/v1/openapi.yaml http://localhost:8080🛠️ 工具和流程
1. 设计工具推荐
| 工具 | 用途 | 推荐度 | 备注 |
|---|---|---|---|
| Swagger Editor | 在线编辑器 | ⭐⭐⭐⭐ | 免费,功能基础 |
| Stoplight Studio | 可视化设计 | ⭐⭐⭐⭐⭐ | 强大的可视化工具 |
| Insomnia Designer | API 设计 | ⭐⭐⭐⭐ | 集成测试功能 |
| VS Code 扩展 | 编辑器集成 | ⭐⭐⭐⭐ | 开发者友好 |
2. Mock 服务工具
bash
# Prism - 基于 OpenAPI 的 Mock 服务
npx @stoplight/prism-cli mock api.yaml --port 3001
# JSON Server - 快速 Mock REST API
npm install -g json-server
json-server --watch mock-data.json --port 3002
# WireMock - 强大的 Mock 服务器
java -jar wiremock-standalone-2.27.2.jar --port 30033. 测试工具集成
yaml
# GitHub Actions CI/CD
name: API Contract Testing
on: [push, pull_request]
jobs:
contract-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Validate OpenAPI Spec
run: |
npx @redocly/cli lint api/openapi.yaml
- name: Run Contract Tests
run: |
# 启动 Mock 服务
npx @stoplight/prism-cli mock api/openapi.yaml --port 3001 &
# 运行契约测试
npm run test:contract
- name: Generate API Documentation
run: |
npx @redocly/cli build-docs api/openapi.yaml -o docs/api.html📈 质量标准
API 设计质量检查清单
设计质量
- [ ] 一致性: API 风格和命名一致
- [ ] 完整性: 包含所有必要的端点和数据
- [ ] 可用性: 接口易于理解和使用
- [ ] 扩展性: 设计支持未来扩展
- [ ] 安全性: 包含适当的安全机制
文档质量
- [ ] 准确性: 文档与实现保持一致
- [ ] 清晰性: 描述清晰,示例完整
- [ ] 完整性: 包含所有必要信息
- [ ] 可测试性: 提供足够信息进行测试
测试覆盖
- [ ] 正常流程: 覆盖所有正常业务流程
- [ ] 异常处理: 覆盖各种错误情况
- [ ] 边界条件: 测试参数边界值
- [ ] 性能测试: 验证关键接口性能
🎯 最佳实践
1. 版本管理策略
yaml
# 语义化版本控制
info:
version: 1.2.3
# 1: 主版本(不兼容的 API 修改)
# 2: 次版本(向下兼容的功能性新增)
# 3: 修订版本(向下兼容的问题修正)
# URL 版本控制
servers:
- url: https://api.example.com/v1
- url: https://api.example.com/v22. 安全设计原则
yaml
# 认证和授权
security:
- bearerAuth: [] # JWT Token
- apiKey: [] # API Key
- oauth2: [read, write] # OAuth2
# 输入验证
parameters:
- name: email
schema:
type: string
format: email
pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'3. 性能考虑
yaml
# 分页设计
parameters:
- name: size
schema:
type: integer
minimum: 1
maximum: 100 # 限制最大页面大小
default: 20
# 字段过滤
parameters:
- name: fields
schema:
type: string
example: "id,name,email"
description: 指定返回字段,减少数据传输💡 API-First + TDD 成功要素
- 团队认同: 所有成员理解并支持这种开发方式
- 工具支持: 选择合适的设计、测试和文档工具
- 流程规范: 建立清晰的设计评审和测试流程
- 持续改进: 根据实践经验不断优化流程