Files
Inbox/Go项目实战/02_接口设计/七七八八的接口设计相关问题.md
2025-12-11 07:24:36 +08:00

175 lines
6.6 KiB
Markdown
Raw Permalink 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: []
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 了。**