Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← Web 开发 / Web Development

Web 平台与 API / Web Platform & APIs

1. 浏览器存储完全指南 / A Complete Guide to Browser Storage

2. 网络基础——10 万长连接是如何拖垮服务器的 / Network Fundamentals and the Cost of One Hundred Thousand Connections

3. API 设计——为什么你的接口总是被吐槽 / API Design for Clear and Evolvable Interfaces

本页目录

API 设计——为什么你的接口总是被吐槽 / API Design for Clear and Evolvable Interfaces ​

📅 创建时间:2026-05-08 🏷️ 标签:#RESTful #GraphQL #OpenAPI #接口设计 #版本管理 #API文档 📚 前置知识:[[00-backend-overview]] [[07-auth-security]](认证鉴权) 📚 相关知识:[[18-observability]](接口日志)


场景:前端工程师和产品经理同时找你吵架 ​

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  前端:我需要按状态筛选订单,你返回的字段不够。          │
│  后端:加一个参数不就行了?                              │
│  前端:但你返回的结构变了,我得改很多地方...             │
│                                                             │
│  产品经理:iOS 和 Android 的接口不一样?                │
│  后端:嗯,历史原因...                                   │
│  产品经理:维护两套接口很累的,能统一吗?                │
│                                                             │
│  新人:我不知道这个接口怎么用,有文档吗?                │
│  后端:Swagger 有...可能没更新...                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14

好的 API 设计 = 好的用户体验 + 低维护成本。


第1节:RESTful 规范——不只是把 URL 改成名词 ​

常见的错误 RESTful ​

❌ 错误示范:
POST /api/addUser          # 用了动词
POST /api/user/add         # 资源命名不一致
GET /api/getUser?id=1     # 动词在 URL 里
GET /api/user/1/orders    # 用嵌套表示关系
POST /api/deleteOrder     # 动作在 URL 里

✅ 正确示范:
POST   /api/users          # 创建用户
GET    /api/users          # 获取用户列表
GET    /api/users/1        # 获取单个用户
PUT    /api/users/1        # 更新用户(完整替换)
PATCH  /api/users/1        # 更新用户(部分更新)
DELETE /api/users/1        # 删除用户
GET    /api/users/1/orders # 获取用户的订单
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

HTTP 方法的语义 ​

┌─────────────────────────────────────────────────────────────┐
│              HTTP 方法语义                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  GET(查询):                                            │
│  → 幂等,多次执行结果相同                               │
│  → 不修改数据                                            │
│  → 可缓存                                                │
│                                                             │
│  POST(创建):                                           │
│  → 非幂等,执行多次产生多个资源                       │
│  → 返回 201 Created + Location header                    │
│                                                             │
│  PUT(完整替换):                                        │
│  → 幂等,多次执行结果相同                               │
│  → 发送完整资源,即使只改一个字段                      │
│  → 缺失字段可能被视为空                                │
│                                                             │
│  PATCH(部分更新):                                      │
│  → 非幂等(通常)                                      │
│  → 只发送需要修改的字段                               │
│  → 推荐用 JSON Merge Patch 或 JSON Patch              │
│                                                             │
│  DELETE(删除):                                         │
│  → 幂等,多次删除结果相同(第二次返回 404)            │
│  → 返回 204 No Content                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

RESTful 响应状态码 ​

┌─────────────────────────────────────────────────────────────┐
│              常用 HTTP 状态码                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  2xx 成功:                                              │
│  200 OK - GET/PUT/PATCH 成功                            │
│  201 Created - POST 创建成功(带 Location)             │
│  204 No Content - DELETE 成功(无返回体)             │
│                                                             │
│  4xx 客户端错误:                                        │
│  400 Bad Request - 参数错误、校验失败                   │
│  401 Unauthorized - 未认证(没登录)                   │
│  403 Forbidden - 无权限(登录了但没权限)              │
│  404 Not Found - 资源不存在                            │
│  409 Conflict - 资源冲突(如重复创建)                │
│  422 Unprocessable Entity - 业务校验失败               │
│  429 Too Many Requests - 限流                          │
│                                                             │
│  5xx 服务端错误:                                        │
│  500 Internal Server Error - 服务器异常                 │
│  502 Bad Gateway - 网关错误                             │
│  503 Service Unavailable - 服务不可用                  │
│  504 Gateway Timeout - 网关超时                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

第2节:RESTful API 实战设计 ​

用户订单 API 设计 ​

yaml
# OpenAPI 3.0 规范
openapi: 3.0.0
info:
  title: 订单服务 API
  version: 1.0.0
  description: 订单创建、查询、取消

paths:
  /users/{userId}/orders:
    get:
      summary: 获取用户的订单列表
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: integer
        - name: status
          in: query
          description: 订单状态筛选
          schema:
            type: string
            enum: [pending, paid, shipped, completed, cancelled]
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: pageSize
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Order'
                  pagination:
                    $ref: '#/components/schemas/Pagination'

    post:
      summary: 创建订单
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - items
                - addressId
              properties:
                items:
                  type: array
                  items:
                    type: object
                    properties:
                      productId:
                        type: integer
                      quantity:
                        type: integer
                addressId:
                  type: integer
      responses:
        '201':
          description: 创建成功
          headers:
            Location:
              schema:
                type: string
              description: 新订单的 URL
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Order'
        '400':
          $ref: '#/components/responses/BadRequest'
        '422':
          $ref: '#/components/responses/Unprocessable'

  /orders/{orderId}:
    get:
      summary: 获取订单详情
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderDetail'
        '404':
          $ref: '#/components/responses/NotFound'

    patch:
      summary: 更新订单(部分更新)
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                status:
                  type: string
                  enum: [cancelled]
                cancelReason:
                  type: string
      responses:
        '200':
          description: 更新成功
        '409':
          description: 状态冲突,无法取消已完成的订单

components:
  schemas:
    Order:
      type: object
      properties:
        id:
          type: integer
        userId:
          type: integer
        status:
          type: string
        totalAmount:
          type: number
          format: decimal
        createdAt:
          type: string
          format: date-time

    OrderDetail:
      allOf:
        - $ref: '#/components/schemas/Order'
        - type: object
          properties:
            items:
              type: array
              items:
                type: object
                properties:
                  productId:
                    type: integer
                  productName:
                    type: string
                  quantity:
                    type: integer
                  price:
                    type: number
            address:
              $ref: '#/components/schemas/Address'

    Pagination:
      type: object
      properties:
        page:
          type: integer
        pageSize:
          type: integer
        total:
          type: integer
        totalPages:
          type: integer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184

第3节:GraphQL vs RESTful——什么时候该用 GraphQL ​

问题:移动端需要不同字段 ​

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  场景:iOS 需要订单列表(字段少)                        │
│        Android 需要订单详情(字段多)                      │
│        Web 需要额外字段(物流信息)                        │
│                                                             │
│  REST 方案:                                              │
│  ❌ 接口爆炸:/orders(列表) /orders/detail /orders/lite│
│  ❌ 字段冗余:列表也返回所有字段                         │
│  ✅ 简单直接,易理解                                      │
│                                                             │
│  GraphQL 方案:                                          │
│  ✅ 单个端点,客户端按需取字段                          │
│  ✅ 一次请求获取多资源                                  │
│  ❌ 学习成本,复杂度高                                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

GraphQL 示例 ​

graphql
# 单个端点:POST /graphql

# 客户端请求(iOS:只需要基础字段)
query {
  orders(status: "pending") {
    id
    totalAmount
    createdAt
  }
}

# 响应
{
  "data": {
    "orders": [
      {"id": 1, "totalAmount": 99.99, "createdAt": "2026-05-08"},
      {"id": 2, "totalAmount": 199.99, "createdAt": "2026-05-08"}
    ]
  }
}

# 客户端请求(Android:需要更多字段)
query {
  orders {
    id
    totalAmount
    items {
      productName
      quantity
    }
    address {
      city
      detail
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

REST vs GraphQL 选择指南 ​

┌─────────────────────────────────────────────────────────────┐
│              REST vs GraphQL 选择                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  选 REST:                                                 │
│  ✅ API 简单,资源明确                                  │
│  ✅ 团队熟悉 HTTP + JSON                                │
│  ✅ 需要 HTTP 缓存(CDN / 浏览器缓存)                  │
│  ✅ 需要清晰的接口文档(Swagger)                       │
│  ✅ 移动端性能要求高(减少数据传输)                    │
│                                                             │
│  选 GraphQL:                                            │
│  ✅ 多端需求差异大(iOS/Android/Web 字段不同)          │
│  ✅ 需要一次请求获取多个相关资源                        │
│  ✅ 前端团队希望自主迭代,不用等后端加字段            │
│  ✅ 数据关系复杂,需要深度查询                          │
│                                                             │
│  实际建议:                                              │
│  → 大部分场景 RESTful 足够                              │
│  → 只有在多端差异化需求严重时才考虑 GraphQL            │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

第4节:API 版本管理——如何平滑升级 ​

三种版本策略 ​

┌─────────────────────────────────────────────────────────────┐
│              API 版本管理策略                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  方案 1:URL 路径版本(最常用)                          │
│  GET /api/v1/users                                        │
│  GET /api/v2/users  ← 新版本                          │
│  ✅ 显式清晰                                              │
│  ❌ 需要维护多套接口                                      │
│                                                             │
│  方案 2:Header 版本(推荐)                             │
│  GET /api/users                                          │
│  API-Version: 2024-01-01                                │
│  ✅ URL 干净                                              │
│  ❌ 不直观,需要看 Header 才能知道版本                   │
│                                                             │
│  方案 3:Query 参数版本(简单项目)                      │
│  GET /api/users?version=2                               │
│  ✅ 最简单                                                │
│  ❌ 容易忘记传版本参数                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

平滑升级策略 ​

python
# 方案:双版本运行,逐步迁移
@app.route('/api/users', methods=['GET'])
def get_users():
    # 读取请求的版本
    api_version = request.headers.get('API-Version', 'v1')

    if api_version.startswith('v2') or api_version >= '2024-01-01':
        return get_users_v2()
    else:
        return get_users_v1()

def get_users_v1():
    """旧版本:返回扁平结构"""
    users = User.query.all()
    return jsonify([{
        'id': u.id,
        'name': u.name,
        'email': u.email
    }])

def get_users_v2():
    """新版本:返回嵌套结构 + 新字段"""
    users = User.query.all()
    return jsonify([{
        'id': u.id,
        'profile': {
            'name': u.name,
            'email': u.email,
            'avatar': u.avatar_url
        },
        'metadata': {
            'createdAt': u.created_at,
            'role': u.role
        }
    }])

# 监控各版本的使用比例
@app.after_request
def track_version(response):
    api_version = request.headers.get('API-Version', 'v1')
    metrics.increment(f'api_version.{api_version}')
    return response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

第5节:接口设计最佳实践 ​

统一响应格式 ​

python
# 统一响应格式
class Response:
    @staticmethod
    def success(data=None, message="操作成功", code=0):
        return jsonify({
            "code": code,
            "message": message,
            "data": data,
            "timestamp": int(time.time())
        })

    @staticmethod
    def error(message, code=1, errors=None):
        return jsonify({
            "code": code,
            "message": message,
            "errors": errors,
            "timestamp": int(time.time())
        }), Response.http_status_for(code)

    @staticmethod
    def http_status_for(code):
        if code == 0:
            return 200
        elif code in [1, 10]:  # 通用错误、业务错误
            return 400
        elif code == 2:  # 未认证
            return 401
        elif code == 3:  # 无权限
            return 403
        elif code == 4:  # 未找到
            return 404
        elif code == 5:  # 限流
            return 429
        else:
            return 500

# 分页响应
class PaginatedResponse:
    @staticmethod
    def success(items, total, page, page_size):
        return Response.success({
            "items": items,
            "pagination": {
                "total": total,
                "page": page,
                "pageSize": page_size,
                "totalPages": (total + page_size - 1) // page_size
            }
        })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

接口安全 Checklist ​

┌─────────────────────────────────────────────────────────────┐
│              接口安全 Checklist                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  认证授权:                                              │
│  ✅ 所有接口需要认证(除登录/注册)                     │
│  ✅ 敏感操作需要二次验证                                 │
│  ✅ 权限校验(用户只能操作自己的资源)                  │
│                                                             │
│  输入校验:                                              │
│  ✅ 参数类型校验(int/string/array)                     │
│  ✅ 参数范围校验(pageSize <= 100)                     │
│  ✅ 字符串长度校验                                       │
│  ✅ SQL / XSS 注入防护                                   │
│                                                             │
│  敏感数据:                                              │
│  ✅ 脱敏展示(手机号 138****5678)                     │
│  ✅ 日志不记录敏感信息                                  │
│  ✅ 接口返回最小必要字段                                │
│                                                             │
│  限流熔断:                                             │
│  ✅ 接口限流([[05-middleware]])                       │
│  ✅ 慢接口熔断(超过 5 秒返回错误)                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

升华:好 API 的标准 ​

┌─────────────────────────────────────────────────────────────┐
│              好 API 的标准                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 可见即可得                                           │
│  → 文档和代码一致                                       │
│  → Swagger/OpenAPI 实时生成文档                         │
│                                                             │
│  2. 自描述                                               │
│  → 字段命名清晰                                          │
│  → 错误信息明确                                          │
│  → 状态码语义正确                                        │
│                                                             │
│  3. 稳定演进                                            │
│  → 版本管理清晰                                          │
│  → 向后兼容                                              │
│  → 渐进迁移                                              │
│                                                             │
│  4. 高效沟通                                            │
│  → 接口变更提前通知                                      │
│  → 提供 SDK / 示例代码                                   │
│                                                             │
│  一句话:                                                │
│  API 是产品,不是实现细节。要像设计产品一样设计 API。    │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

"AI 可查 vs 必须理解"清单 ​

AI 可查:
✅ OpenAPI 规范的完整语法
✅ GraphQL Schema 的详细定义
✅ 各语言的分页实现方式

必须理解:
🔴 RESTful 的 HTTP 方法语义(GET/POST/PUT/PATCH/DELETE)
🔴 PUT vs PATCH 的区别
🔴 GraphQL 适用场景(不是所有场景都适合)
🔴 API 版本管理策略的选择
🔴 统一响应格式的重要性
1
2
3
4
5
6
7
8
9
10
11

学习状态:🟡 开始学习

最后更新于:

Pager
上一篇2. 网络基础——10 万长连接是如何拖垮服务器的 / Network Fundamentals and the Cost of One Hundred Thousand Connections

持续记录,持续成长

Copyright © Tidenflow