This repository has been archived on 2026-05-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
obsidian/BACKEND/API 设计.md
T

183 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [后端, API, RESTful, 设计]
create time: 2026-04-24 18:41
---
# API 设计
## 概述
API(Application Programming Interface)是前后端协作的契约。好的 API 设计让前端开发者一看就懂、一用就会。差的 API 设计会让协作效率大幅下降。
思考题:设计一个 API 时,是应该"让所有人都符合你的设计"还是"让设计迁就所有使用场景"?这两者的边界在哪里?
## 正文
### 1. RESTful API 设计规范
**资源命名:**
- URL 用名词复数,不用动词
- 层级结构表示从属关系
- 查询参数用于筛选和排序
```
GET /api/users → 获取用户列表
GET /api/users/5 → 获取用户 5 的详情
GET /api/users/5/posts → 获取用户 5 的所有文章
GET /api/users?role=admin → 筛选管理员用户
GET /api/users?sort=-created_at → 按创建时间倒序
GET /api/users?page=2&limit=20 → 分页
```
### 2. 标准响应格式
```json
// 成功响应(200 OK)
{
"code": 0,
"message": "success",
"data": {
"id": 1,
"name": "Alice",
"email": "alice@example.com"
}
}
// 成功响应(201 Created)
{
"code": 0,
"message": "created",
"data": { "id": 5, ... }
}
// 错误响应(4xx / 5xx)
{
"code": 1001,
"message": "用户不存在",
"data": null
}
```
> **提问:** 错误码设计成数字好还是字符串好?数字更紧凑但可读性差,字符串更直观但传输量大。你怎么权衡?
### 3. 常见错误码设计
| code | HTTP 状态码 | 含义 |
|------|-------------|------|
| 0 | 200 | 成功 |
| 1001 | 404 | 资源不存在 |
| 1002 | 400 | 参数校验失败 |
| 1003 | 401 | 未认证 |
| 1004 | 403 | 无权限 |
| 1005 | 429 | 请求过于频繁 |
| 2001 | 500 | 服务器内部错误 |
### 4. 分页设计
```
GET /api/users?page=1&limit=20
响应:
{
"code": 0,
"data": [...], // 当前页数据
"pagination": {
"page": 1,
"limit": 20,
"total": 100,
"total_pages": 5
}
}
```
> **核心概念:** 客户端分页 vs 服务端分页。大数据量时必须服务端分页,否则性能灾难。
### 5. 版本控制
```
方案一:URL 路径(推荐)
/api/v1/users
/api/v2/users
方案二:请求头
Accept: application/vnd.api.v1+json
```
### 6. Go 后端 API 示例
```go
package main
import (
"encoding/json"
"net/http"
"time"
)
// 统一响应结构
type APIResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func writeJSON(w http.ResponseWriter, status int, resp APIResponse) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(resp)
}
// 处理用户列表
func userListHandler(w http.ResponseWriter, r *http.Request) {
// 1. 解析查询参数
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
if page < 1 {
page = 1
}
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
if limit == 0 {
limit = 20
}
// 2. 查询数据库(省略)
users, total := getUsersFromDB(page, limit)
// 3. 返回统一格式
writeJSON(w, http.StatusOK, APIResponse{
Code: 0,
Message: "success",
Data: map[string]interface{}{
"users": users,
"pagination": map[string]int{
"page": page,
"limit": limit,
"total": total,
"pages": (total + limit - 1) / limit,
},
},
})
}
```
```mermaid
graph TD
A[HTTP Request] --> B{方法检查}
B -->|GET| C[解析查询参数]
B -->|POST| D[解析请求体]
C --> E[参数校验]
D --> E
E --> F{校验通过?}
F -->|否| G[返回 400 + 错误信息]
F -->|是| H[查询/操作数据库]
H --> I{操作成功?}
I -->|否| J[返回对应错误码]
I -->|是| K[返回 200/201 + 数据]
```
## 关联笔记
- [[HTTP 协议]] — API 设计的基础是理解 HTTP
- [[Go 后端基础]] — Go 实现 API 的具体方法
- [[数据库基础]] — API 的数据来源
- [[30.areas/finance/Investment lessons/2024.Current trading lessons.md]]