175 lines
6.6 KiB
Markdown
175 lines
6.6 KiB
Markdown
---
|
||
tags: []
|
||
aliases:
|
||
- 1. 接口版本控制策略 (API Versioning Strategy)
|
||
date created: 星期日, 十二月 7日 2025, 11:38:52 晚上
|
||
date modified: 星期日, 十二月 7日 2025, 11:42:18 晚上
|
||
---
|
||
|
||
# 七七八八的接口设计相关问题
|
||
|
||
## 1. 接口版本控制策略 (API Versioning Strategy)
|
||
|
||
**核心问题:** 当你发布了 V1 版本后,某天需要修改接口字段(比如把 `name` 拆分为 `first_name` 和 `last_name`),如何保证老版本的 App 不会崩溃?
|
||
|
||
**三种主流流派:**
|
||
|
||
1. **URI Path Versioning (推荐):**
|
||
|
||
- **格式:** `https://api.example.com/v1/users`
|
||
- **优点:** 直观、易于调试、缓存友好。这也是 GitHub, Twitter, Google API 采用的主流方案。
|
||
- **落地:** 我们在 Gin 的 Router Group 中直接体现:
|
||
|
||
Go
|
||
|
||
```bash
|
||
v1 := r.Group("/api/v1")
|
||
{
|
||
v1.GET("/users", ...)
|
||
}
|
||
```
|
||
|
||
2. **Header Versioning:**
|
||
|
||
- **格式:** Header 中添加 `Accept: application/vnd.myapi.v1+json`
|
||
- **优点:** URL 干净。
|
||
- **缺点:** 调试麻烦(浏览器直接访问 URL 看不到结果),CDN 缓存配置复杂。**不推荐 MVP 阶段使用。**
|
||
|
||
3. **Query Parameter:**
|
||
|
||
- **格式:** `/users?version=1`
|
||
- **评价:** 看起来很土,通常不用于 RESTful API。
|
||
|
||
**👉 你的策略:** 坚定选择 **URI Path Versioning (`/api/v1`)**。只在发生**破坏性变更 (Breaking Change)** 时才升级到 v2。新增字段不算破坏性变更,不需要升级版本。
|
||
|
||
---
|
||
|
||
## 2. HTTP 方法的精准语义 (Verbs Semantics)
|
||
|
||
很多新手只会用 `GET` 和 `POST`。企业级 API 必须精准区分以下方法的含义:
|
||
|
||
|**方法**|**语义**|**幂等性 (Idempotency)**|**典型场景**|
|
||
|---|---|---|---|
|
||
|**GET**|获取资源|✅ 是|获取文章列表、详情|
|
||
|**POST**|新建资源|❌ 否|发布新文章、提交评论|
|
||
|**PUT**|**全量替换**资源|✅ 是|修改文章(客户端发送文章的完整 JSON,没传的字段会被置空)|
|
||
|**PATCH**|**局部更新**资源|❌ 否 (理论上)|修改文章状态(只传 `{"status": "published"}`,其他字段不变)|
|
||
|**DELETE**|删除资源|✅ 是|删除文章|
|
||
|
||
⚠️ 重点关注 PUT vs PATCH:
|
||
|
||
在 Go 语言中实现 PATCH 有点麻烦(因为 Go 的结构体默认值问题,你很难区分用户是传了 0 还是没传这个字段)。
|
||
|
||
- **最佳实践:** 对于 CMS 这种表单复杂的系统,**修改接口首选 `PUT` (全量)**,或者针对特定状态修改提供独立接口(如 `POST /articles/:id/publish`)。如果必须做 `PATCH`,DTO 需使用指针类型 `*string` 来判断是否为 `nil`。
|
||
|
||
---
|
||
|
||
## 3. RESTful URL 设计模式 (Resource Naming)
|
||
|
||
**原则:URL 中只出现名词,不出现动词。**
|
||
|
||
- ❌ **反例 (RPC 风格 - 不要这么做):**
|
||
- `/api/getUsers`
|
||
- `/api/createUser`
|
||
- `/api/deleteArticle?id=1`
|
||
- ✅ **正例 (REST 风格):**
|
||
- `GET /api/v1/users` (获取列表)
|
||
- `POST /api/v1/users` (创建)
|
||
- `DELETE /api/v1/articles/1` (删除 ID 为 1 的文章)
|
||
|
||
**复杂关系的嵌套设计:**
|
||
|
||
- _场景:_ 获取某篇文章下的评论。
|
||
- _设计:_ `GET /api/v1/articles/{article_id}/comments`
|
||
- _场景:_ 获取某个作者的所有文章。
|
||
- _设计:_ `GET /api/v1/users/{user_id}/articles`
|
||
|
||
---
|
||
|
||
## 4. 列表接口三剑客:分页、排序、筛选 (Pagination, Sorting, Filtering)
|
||
|
||
你的 CMS 一定会有“文章列表”页面,这个接口是最复杂的。不要为每种查询都写一个新接口,要设计一个**通用的查询接口**。
|
||
|
||
**最佳实践标准:**
|
||
|
||
1. **分页 (Pagination):**
|
||
|
||
- 使用 `page` (页码) 和 `page_size` (每页条数)。
|
||
- URL 示例: `/articles?page=2&page_size=20`
|
||
- **注意:** 要限制 `page_size` 的最大值(如 100),防止恶意用户一次请求 100 万条数据把数据库打挂。
|
||
|
||
2. **排序 (Sorting):**
|
||
|
||
- 使用 `sort` 参数。`-` 代表降序,无符号代表升序。
|
||
- URL 示例: `/articles?sort=-created_at` (按创建时间倒序)
|
||
- URL 示例: `/articles?sort=view_count,-created_at` (先按浏览量升序,再按时间倒序)
|
||
|
||
3. **筛选 (Filtering):**
|
||
|
||
- 直接使用字段名作为参数。
|
||
- URL 示例: `/articles?category_id=1&status=published`
|
||
|
||
---
|
||
|
||
## 5. 状态码与错误处理 (Status Codes & Error Handling)
|
||
|
||
**不要永远只返回 200 OK!**
|
||
|
||
前端开发最恨的就是:HTTP 状态码是 200,结果 Body 里写着 `{"code": 500, "msg": "Error"}`。这会让监控系统失效。
|
||
|
||
**你需要遵守的“HTTP 状态码地图”:**
|
||
|
||
- **2xx (成功):**
|
||
- `200 OK`: 通用成功。
|
||
- `201 Created`: 创建成功 (POST 返回)。
|
||
- `204 No Content`: 删除成功 (DELETE 返回,不带 Body)。
|
||
- **4xx (客户端错误 - 前端背锅):**
|
||
- `400 Bad Request`: 参数校验失败(如邮箱格式不对)。
|
||
- `401 Unauthorized`: 未登录/Token 过期。
|
||
- `403 Forbidden`: 登录了,但没权限(如普通用户想删文章)。
|
||
- `404 Not Found`: 资源不存在。
|
||
- **5xx (服务端错误 - 你背锅):**
|
||
- `500 Internal Server Error`: 代码崩了/数据库挂了。
|
||
|
||
统一错误响应格式 (JSON Envelope):
|
||
|
||
无论发生什么错误,Body 必须保持结构一致,方便前端拦截:
|
||
|
||
JSON
|
||
|
||
```bash
|
||
{
|
||
"code": 40001, // 业务错误码 (你在 pkg/e 定义的)
|
||
"message": "标题已存在", // 给用户看的提示
|
||
"request_id": "abc-123" // 方便查日志的 Trace ID
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 进阶知识:幂等性 (Idempotency)
|
||
|
||
**概念:** 用户因为网络卡顿,连续点击了 3 次“发布文章”按钮,你的系统会创建 1 篇文章还是 3 篇?
|
||
|
||
- **非幂等:** `POST`。点 3 次创建 3 篇。
|
||
- **幂等:** `PUT` / `DELETE`。改 3 次和改 1 次结果一样;删 3 次和删 1 次结果一样(都是没了)。
|
||
|
||
如何处理 POST 的幂等性?
|
||
|
||
对于关键业务(如支付、核心数据创建),通常要求前端在 Header 带一个 Idempotency-Key (UUID)。后端 Redis 记录这个 Key,如果短时间内收到相同的 Key,直接返回上次的结果,不执行数据库插入。
|
||
|
||
(注:MVP 阶段可以先不做,但面试或者做高可靠系统时必须知道)
|
||
|
||
---
|
||
|
||
## 总结:在你开始定义 API 之前
|
||
|
||
请在心中建立这套标准:
|
||
|
||
1. **URL:** `/api/v1/名词`
|
||
2. **Method:** 查询用 GET,新建用 POST,修改用 PUT,删除用 DELETE。
|
||
3. **List:** 永远记得预留 `page`, `page_size`。
|
||
4. **Response:** 永远返回统一的 JSON 结构,报错必须用 4xx/5xx 状态码。
|
||
|
||
**准备好了吗?我们可以开始基于这些原则,设计 User 模块的 API 了。**
|