feat: 更新附录文档及对应交互组件
This commit is contained in:
@@ -1,179 +1,35 @@
|
||||
# API 入门
|
||||
# API 入门:从零理解"程序之间的对话"
|
||||
|
||||
::: tip 🎯 学习目标
|
||||
阅读完本节后,你将能够:
|
||||
- 理解 API 的本质概念和设计哲学
|
||||
- 区分不同类型的 API(函数 API、操作系统 API、Web API)
|
||||
- 掌握 HTTP 方法的语义和使用场景
|
||||
- 学会阅读和使用 API 文档
|
||||
- 理解 HTTP 调用与 SDK 调用的区别
|
||||
::: tip 🎯 核心问题
|
||||
**什么是 API?** 这就像问:餐厅的菜单怎么设计,客人一看就懂?服务员怎么记单,不会出错?API 解决的就是"程序之间如何对话"的问题。你写代码的第一天就在用 API,只是你可能没意识到。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 1. 从一个按钮开始
|
||||
## 0. 新手常见的三个困惑
|
||||
|
||||
<ApiQuickStartDemo />
|
||||
**困惑一:API 是很高深的东西吗?**
|
||||
|
||||
👆 看见了吗?点击按钮,几百毫秒后,服务器返回了当前时间。
|
||||
|
||||
这个过程,就是一次完整的 **API 调用**。虽然简单,但它包含了 API 交互的所有核心要素:
|
||||
|
||||
| 阶段 | 发生了什么 |
|
||||
|------|-----------|
|
||||
| **请求** | 客户端向服务器发送 "给我当前时间" 的请求 |
|
||||
| **处理** | 服务器接收请求,查询系统时间 |
|
||||
| **响应** | 服务器将时间数据返回给客户端 |
|
||||
|
||||
这就像你去便利店买水——你说"来瓶可乐",店员给你一瓶可乐。你问一句,他给一笔。
|
||||
|
||||
**API(Application Programming Interface,应用程序编程接口)**,本质上就是一种"对话约定":一方提出请求,另一方给出响应。
|
||||
|
||||
---
|
||||
|
||||
## 2. API 的三种形态
|
||||
|
||||
很多人一提到 API,就觉得这是很高深的东西。其实,你写代码的第一天就在用 API 了。
|
||||
|
||||
### 2.1 函数 API:最基础的形态
|
||||
|
||||
<FunctionApiDemo />
|
||||
|
||||
当你调用 `len("hello")` 时,你就在使用 Python 提供的一个 API。你不需要知道 `len()` 内部是怎么数字符串长度的——是用 C 实现的还是用 Python 实现的?是遍历计数还是直接读取长度字段?这些细节都被隐藏了。
|
||||
|
||||
**API 的核心价值:把复杂的东西藏起来,只给你一个简单的用法。**
|
||||
|
||||
再看一个更贴近实际的例子:
|
||||
很多人一听到 API,就觉得是高级工程师才能理解的概念。其实你早就用过 API 了:
|
||||
|
||||
```python
|
||||
# 你写这行代码
|
||||
response = requests.get("https://api.example.com/users")
|
||||
|
||||
# 背后发生了什么?
|
||||
# 1. DNS 解析:把域名转成 IP 地址
|
||||
# 2. TCP 连接:建立网络通道
|
||||
# 3. TLS 握手:加密通信
|
||||
# 4. HTTP 请求:发送数据包
|
||||
# 5. 等待响应:接收服务器返回
|
||||
# 6. 解析数据:把字节流转成 Python 对象
|
||||
len("hello") # 这就是 Python 提供的 API
|
||||
open("file.txt") # 这也是 API
|
||||
requests.get(url) # 这还是 API
|
||||
```
|
||||
|
||||
如果你要自己处理这 6 步,每次请求都要写几百行代码。但 `requests.get()` 把这些都封装好了,你只需要一行代码。
|
||||
**困惑二:Web API 和普通 API 有什么区别?**
|
||||
|
||||
### 2.2 操作系统 API:让程序能"碰"硬件
|
||||
| 类型 | 调用对象 | 通信方式 | 典型场景 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **函数 API** | 本地代码 | 函数调用 | `len()`, `open()` |
|
||||
| **操作系统 API** | 操作系统 | 系统调用 | 读写文件、创建进程 |
|
||||
| **Web API** | 远程服务器 | HTTP 请求 | 调用 AI 模型、获取天气 |
|
||||
|
||||
当你在电脑上打开一个文件:
|
||||
**困惑三:我该用 HTTP 还是 SDK?**
|
||||
|
||||
```python
|
||||
with open("file.txt", "r") as f:
|
||||
content = f.read()
|
||||
```
|
||||
|
||||
这行代码背后,Python 调用了**操作系统的 API**。
|
||||
|
||||
| 操作系统 | API 名称 | 作用 |
|
||||
|---------|---------|------|
|
||||
| Windows | Win32 API | 文件操作、窗口管理、进程控制 |
|
||||
| Linux | POSIX API | 系统调用、进程通信、设备访问 |
|
||||
| macOS | Cocoa API | 图形界面、文件系统、网络 |
|
||||
|
||||
没有操作系统 API,你的 Python 代码就是一堆文字,根本动不了硬盘里的文件、显示不了窗口、连不上网络。
|
||||
|
||||
### 2.3 Web API:跨越网络的"超能力"
|
||||
|
||||
最后,才是大多数人熟知的 Web API:
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
response = requests.post(
|
||||
"https://api.deepseek.com/v1/chat/completions",
|
||||
headers={"Authorization": "Bearer sk-xxx"},
|
||||
json={
|
||||
"model": "deepseek-chat",
|
||||
"messages": [{"role": "user", "content": "你好"}]
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
这行代码做了什么?它让你的程序穿越互联网,调用了千里之外 DeepSeek 服务器上的 AI 模型。就像你打了个越洋电话,让大洋彼岸的厨师帮你做了一道菜。
|
||||
|
||||
**Web API 的神奇之处:让"调用别人的超级电脑"变得像调用本地函数一样简单。**
|
||||
|
||||
---
|
||||
|
||||
## 3. API 的统一结构
|
||||
|
||||
无论哪种 API,结构都一样。就像插头和插座,怎么变都离不开三样东西:
|
||||
|
||||
<ApiConceptDemo />
|
||||
|
||||
| 要素 | 函数 API | Web API |
|
||||
|------|---------|---------|
|
||||
| **地址/名称** | `len()` | `https://api.example.com/users` |
|
||||
| **输入/参数** | `"hello"` | `{"name": "张三"}` |
|
||||
| **输出/返回** | `5` | `{"id": 1, "name": "张三"}` |
|
||||
|
||||
理解了这个统一结构,你就掌握了 API 的本质。无论是调用一个 Python 函数,还是请求一个远程服务器,思路都是一样的:
|
||||
|
||||
1. **找到入口**(函数名或 URL)
|
||||
2. **传入参数**(按要求的格式)
|
||||
3. **处理返回**(按约定的结构解析)
|
||||
|
||||
---
|
||||
|
||||
## 4. HTTP 方法:你是在"问"还是在"做"?
|
||||
|
||||
调用 Web API 时,你需要告诉服务器你想做什么。这就是 HTTP 方法的由来。
|
||||
|
||||
想象你去一家餐厅:
|
||||
|
||||
| 场景 | 现实中你会怎么说? | 对应的 HTTP 方法 |
|
||||
|------|-------------------|-----------------|
|
||||
| 你想知道今天有什么菜 | "服务员,菜单给我看看" | **GET** - 纯"问",不改数据 |
|
||||
| 你想点一份宫保鸡丁 | "给我来份宫保鸡丁" | **POST** - "做"件事,创建数据 |
|
||||
| 你想换一道菜 | "把宫保鸡丁改成糖醋里脊" | **PUT** - 替换数据 |
|
||||
| 你想改口味 | "宫保鸡丁不要放花生" | **PATCH** - 部分修改 |
|
||||
| 你不想要了 | "算了,那道菜不要了" | **DELETE** - 删除数据 |
|
||||
|
||||
<ApiMethodDemo />
|
||||
|
||||
::: warning 关于幂等性
|
||||
**幂等性**是一个重要概念:多次执行结果是否相同?
|
||||
|
||||
- **GET**:查询 10 次和查询 1 次,结果一样 → 幂等
|
||||
- **DELETE**:删除 10 次和删除 1 次,结果一样 → 幂等
|
||||
- **POST**:下单 10 次,可能创建 10 个订单 → 不幂等
|
||||
|
||||
实际开发中,POST 操作通常需要用唯一 ID 来防止重复处理。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. HTTP vs SDK:自己跑腿还是让管家代办?
|
||||
|
||||
在 AI 编程教程中,你会经常看到两种调用方式:**HTTP** 和 **SDK**。它们的区别就像"自己跑腿"和"让管家代办"。
|
||||
|
||||
<RealWorldApiDemo />
|
||||
|
||||
### 5.1 HTTP API:自己跑腿
|
||||
|
||||
这是最原始的方式。你需要:
|
||||
|
||||
1. **找到网址**(Base URL + Endpoint)
|
||||
2. **准备请求头**(Authorization、Content-Type)
|
||||
3. **构造请求体**(JSON 格式的参数)
|
||||
4. **发送请求**(处理网络错误、超时)
|
||||
5. **解析响应**(把 JSON 转成可用的数据)
|
||||
|
||||
所有编程语言都能用,但你需要处理很多细节。
|
||||
|
||||
### 5.2 SDK:让管家代办
|
||||
|
||||
SDK(Software Development Kit)就像是 API 提供方派给你的专属管家:
|
||||
|
||||
```python
|
||||
# HTTP 方式:自己处理所有细节
|
||||
# HTTP 方式:自己处理所有细节
|
||||
import requests
|
||||
response = requests.post(
|
||||
"https://api.deepseek.com/v1/chat/completions",
|
||||
@@ -182,7 +38,7 @@ response = requests.post(
|
||||
)
|
||||
result = response.json()["choices"][0]["message"]["content"]
|
||||
|
||||
# SDK 方式:管家帮你处理
|
||||
# SDK 方式:管家帮你处理
|
||||
from openai import OpenAI
|
||||
client = OpenAI(api_key="sk-xxx")
|
||||
response = client.chat.completions.create(
|
||||
@@ -192,94 +48,219 @@ response = client.chat.completions.create(
|
||||
result = response.choices[0].message.content
|
||||
```
|
||||
|
||||
SDK 自动处理了:鉴权、请求格式、错误处理、重试逻辑、响应解析。
|
||||
---
|
||||
|
||||
::: tip 建议
|
||||
**能用 SDK 就用 SDK**,把麻烦事留给库,把时间留给自己。
|
||||
## 1. API 的本质:插头与插座
|
||||
|
||||
**API**(Application Programming Interface,应用程序编程接口)就是"程序之间对话的约定"。
|
||||
|
||||
### 1.1 用电器来类比
|
||||
|
||||
| 概念 | 电器类比 | API 对应 |
|
||||
| :--- | :--- | :--- |
|
||||
| **接口** | 插座形状 | 函数签名 / URL |
|
||||
| **输入** | 电流输入 | 函数参数 / 请求体 |
|
||||
| **输出** | 电器工作 | 返回值 / 响应体 |
|
||||
|
||||
### 1.2 三种 API 形态对比
|
||||
|
||||
<ApiTypesComparison />
|
||||
|
||||
---
|
||||
|
||||
## 2. 一次完整的 API 调用
|
||||
|
||||
👇 **动手试试看**:点击下方按钮,观察一次完整的 API 请求-响应流程:
|
||||
|
||||
<ApiRequestDemo />
|
||||
|
||||
### 2.1 API 调用的四个阶段
|
||||
|
||||
| 阶段 | 发生了什么 | 电器类比 |
|
||||
| :--- | :--- | :--- |
|
||||
| **请求** | 客户端向服务器发送请求 | 按下开关 |
|
||||
| **传输** | 请求通过网络传输到服务器 | 电流通过电线 |
|
||||
| **处理** | 服务器处理请求并返回数据 | 电器开始工作 |
|
||||
| **响应** | 客户端接收并处理返回结果 | 灯泡发光 |
|
||||
|
||||
### 2.2 餐厅类比
|
||||
|
||||
| 餐厅角色 | API 对应 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| **菜单** | API 文档 | 告诉你有哪些"菜"可以点 |
|
||||
| **服务员** | HTTP 协议 | 标准化的"对话方式" |
|
||||
| **后厨** | 服务端 | 按"订单"处理请求 |
|
||||
| **上菜** | 响应 | 把结果返回给"客人" |
|
||||
|
||||
---
|
||||
|
||||
## 3. HTTP 方法:你是在"问"还是在"做"?
|
||||
|
||||
调用 Web API 时,你需要告诉服务器你想做什么。这就是 HTTP 方法的由来。
|
||||
|
||||
### 3.1 用餐厅点餐来理解
|
||||
|
||||
| 场景 | 现实中你会怎么说? | 对应的 HTTP 方法 |
|
||||
| :--- | :--- | :--- |
|
||||
| 你想知道今天有什么菜 | "服务员,菜单给我看看" | **GET** - 纯"问",不改数据 |
|
||||
| 你想点一份宫保鸡丁 | "给我来份宫保鸡丁" | **POST** - "做"件事,创建数据 |
|
||||
| 你想换一道菜 | "把宫保鸡丁改成糖醋里脊" | **PUT** - 替换数据 |
|
||||
| 你想改口味 | "宫保鸡丁不要放花生" | **PATCH** - 部分修改 |
|
||||
| 你不想要了 | "算了,那道菜不要了" | **DELETE** - 删除数据 |
|
||||
|
||||
<HttpMethodsDemo />
|
||||
|
||||
::: warning 关于幂等性
|
||||
**幂等性**:多次执行结果是否相同?
|
||||
|
||||
- **幂等的操作**(GET/PUT/DELETE):点 10 次和点 1 次,结果一样
|
||||
- **不幂等的操作**(POST):点 10 次,可能创建 10 个订单
|
||||
|
||||
**解决方案**:POST 操作用唯一 ID 校验,避免重复处理。
|
||||
:::
|
||||
|
||||
### 3.2 HTTP 方法速查表
|
||||
|
||||
| 方法 | 用途 | 幂等性 | 安全性 | 典型场景 |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **GET** | 获取资源 | 是 | 是 | 查询列表、查看详情 |
|
||||
| **POST** | 创建资源 | 否 | 否 | 新增用户、提交订单 |
|
||||
| **PUT** | 全量更新 | 是 | 否 | 替换整个用户资料 |
|
||||
| **PATCH** | 部分更新 | 否 | 否 | 只修改昵称 |
|
||||
| **DELETE** | 删除资源 | 是 | 否 | 删除用户、取消订单 |
|
||||
|
||||
---
|
||||
|
||||
## 4. HTTP 状态码:服务器在告诉你什么?
|
||||
|
||||
服务器回复时,会先返回一个状态码,告诉你请求是否成功。
|
||||
|
||||
### 4.1 状态码分类
|
||||
|
||||
<StatusCodeCategories />
|
||||
|
||||
### 4.2 常见状态码详解
|
||||
|
||||
| 状态码 | 含义 | 典型场景 | 客户端处理 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **200 OK** | 成功 | 请求正常处理 | 展示数据 |
|
||||
| **201 Created** | 创建成功 | POST 请求成功创建资源 | 跳转到新资源 |
|
||||
| **400 Bad Request** | 请求格式错误 | 参数缺失或格式不对 | 检查参数 |
|
||||
| **401 Unauthorized** | 未认证 | 没有提供有效的 API Key | 引导用户登录 |
|
||||
| **403 Forbidden** | 无权限 | API Key 没有访问该资源的权限 | 提示权限不足 |
|
||||
| **404 Not Found** | 不存在 | 请求的地址或资源不存在 | 检查 URL |
|
||||
| **429 Too Many Requests** | 请求过多 | 超过了速率限制 | 稍后重试 |
|
||||
| **500 Internal Server Error** | 服务器错误 | 服务端出了问题 | 提示用户稍后重试 |
|
||||
|
||||
👇 **动手试试看**:点击下方按钮,了解常见状态码的含义:
|
||||
|
||||
<StatusCodeDemo />
|
||||
|
||||
---
|
||||
|
||||
## 5. HTTP vs SDK:自己跑腿还是让管家代办?
|
||||
|
||||
### 5.1 两种调用方式对比
|
||||
|
||||
| | 🏃 **HTTP API** | 🤵 **SDK** |
|
||||
| :--- | :--- | :--- |
|
||||
| **比喻** | 自己跑腿 | 管家代办 |
|
||||
| **优点** | ✓ 所有语言都能用<br>✓ 完全控制请求细节<br>✓ 无需额外依赖 | ✓ 代码简洁易读<br>✓ 自动处理鉴权<br>✓ 内置错误重试 |
|
||||
| **缺点** | ✗ 需要处理所有细节<br>✗ 代码冗长易出错 | ✗ 需要安装依赖<br>✗ 可能有版本问题 |
|
||||
| **代码示例** | `requests.post(url, json=..., headers={...})` | `client.chat.completions.create(...)` |
|
||||
|
||||
### 5.2 如何选择?
|
||||
|
||||
| 场景 | 推荐方式 | 原因 |
|
||||
| :--- | :--- | :--- |
|
||||
| **快速开发** | SDK | 自动处理鉴权、错误、重试 |
|
||||
| **学习原理** | HTTP | 理解底层机制 |
|
||||
| **不支持的语言** | HTTP | 任何语言都能用 |
|
||||
| **需要定制** | HTTP | 灵活控制每个细节 |
|
||||
|
||||
::: tip 💡 建议
|
||||
**能用 SDK 就用 SDK**,把麻烦事留给库,把时间留给自己。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 6. 如何阅读 API 文档?
|
||||
## 6. 如何阅读 API 文档?
|
||||
|
||||
API 文档就像说明书和菜单的结合体。你不需要从头读到尾,只需要学会"查字典"。
|
||||
|
||||
打开任何一个 API 文档(比如 OpenAI 或 DeepSeek),你只需要找这几样东西:
|
||||
|
||||
<ApiDocumentDemo />
|
||||
API 文档就像说明书和菜单的结合体。你不需要从头读到尾,只需要学会"查字典"。
|
||||
|
||||
### 6.1 文档阅读清单
|
||||
|
||||
打开任何一个 API 文档(比如 OpenAI 或 DeepSeek),你只需要找这几样东西:
|
||||
|
||||
<ApiDocumentDemo />
|
||||
|
||||
| 项目 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| :--- | :--- | :--- |
|
||||
| **Base URL** | API 的根地址 | `https://api.deepseek.com` |
|
||||
| **Authentication** | 如何证明身份 | `Authorization: Bearer sk-xxx` |
|
||||
| **Endpoints** | 具体的接口列表 | `/v1/chat/completions` |
|
||||
| **Parameters** | 必填/可选参数 | `model`(必填)、`temperature`(可选) |
|
||||
| **Parameters** | 必填/可选参数 | `model`(必填)、`temperature`(可选) |
|
||||
| **Response** | 返回数据结构 | `{"choices": [...]}` |
|
||||
|
||||
### 6.2 常见状态码
|
||||
### 6.2 阅读文档的步骤
|
||||
|
||||
服务器回复时,会先返回一个状态码,告诉你请求是否成功:
|
||||
|
||||
| 状态码 | 含义 | 常见原因 |
|
||||
|--------|------|---------|
|
||||
| **200 OK** | 成功 | 请求正常处理 |
|
||||
| **201 Created** | 创建成功 | POST 请求成功创建资源 |
|
||||
| **400 Bad Request** | 请求格式错误 | 参数缺失或格式不对 |
|
||||
| **401 Unauthorized** | 未认证 | 没有提供有效的 API Key |
|
||||
| **403 Forbidden** | 无权限 | API Key 没有访问该资源的权限 |
|
||||
| **404 Not Found** | 不存在 | 请求的地址或资源不存在 |
|
||||
| **429 Too Many Requests** | 请求过多 | 超过了速率限制 |
|
||||
| **500 Internal Server Error** | 服务器错误 | 服务端出了问题 |
|
||||
1. **找到 Base URL** - 这是所有请求的前缀
|
||||
2. **看懂认证方式** - API Key 放在 Header 还是 Query?
|
||||
3. **找到需要的 Endpoint** - 你要调用的具体接口
|
||||
4. **查看请求参数** - 哪些必填?哪些可选?
|
||||
5. **理解返回格式** - 数据是如何组织的?
|
||||
|
||||
---
|
||||
|
||||
## 7. 动手练习
|
||||
## 7. 动手练习:模拟 API 调用
|
||||
|
||||
光说不练假把式。这里有个模拟 API,你可以随便填参数、随便改地址,看看会发生什么。
|
||||
光说不练假把式。这里有个模拟 API,你可以随便填参数、随便改地址,看看会发生什么。
|
||||
|
||||
<ApiPlayground />
|
||||
|
||||
试着触发以下场景:
|
||||
- ✅ **成功请求**:填入正确的 Endpoint 和 API Key
|
||||
- ❌ **401 错误**:不填 API Key,看看服务器怎么拒绝你
|
||||
- ❌ **404 错误**:填一个不存在的地址
|
||||
试着触发以下场景:
|
||||
- ✅ **成功请求**:填入正确的 Endpoint 和 API Key
|
||||
- ❌ **401 错误**:不填 API Key,看看服务器怎么拒绝你
|
||||
- ❌ **404 错误**:填一个不存在的地址
|
||||
|
||||
---
|
||||
|
||||
## 8. 小结
|
||||
|
||||
::: info 核心要点
|
||||
1. **API 就是传声筒**,帮你把话传给另一段代码或远程服务器
|
||||
2. **你早就用过 API 了**,从 `len()` 到 `open()` 都是 API
|
||||
3. **Web API 是超能力**,让你调用千里之外的超级电脑
|
||||
4. **SDK 是好管家**,能用 SDK 就别自己跑腿
|
||||
5. **看文档找三样**:地址、鉴权、参数
|
||||
1. **API 就是传声筒**,帮你把话传给另一段代码或远程服务器
|
||||
2. **你早就用过 API 了**,从 `len()` 到 `open()` 都是 API
|
||||
3. **Web API 是超能力**,让你调用千里之外的超级电脑
|
||||
4. **SDK 是好管家**,能用 SDK 就别自己跑腿
|
||||
5. **看文档找三样**:地址、鉴权、参数
|
||||
:::
|
||||
|
||||
在 AI 编程的时代,你只需要记住这几个核心概念。剩下的细节,IDE 和 AI 助手会帮你处理。
|
||||
在 AI 编程的时代,你只需要记住这几个核心概念。剩下的细节,IDE 和 AI 助手会帮你处理。
|
||||
|
||||
---
|
||||
|
||||
## 名词速查表
|
||||
|
||||
| 名词 | 全称 | 解释 |
|
||||
|------|------|------|
|
||||
| **API** | Application Programming Interface | 应用程序编程接口,定义了软件之间如何交互 |
|
||||
| **Web API** | - | 基于 HTTP 协议的 API,用于网络通信 |
|
||||
| **Endpoint** | - | 端点,API 的具体地址 |
|
||||
| :--- | :--- | :--- |
|
||||
| **API** | Application Programming Interface | 应用程序编程接口,定义了软件之间如何交互 |
|
||||
| **Web API** | - | 基于 HTTP 协议的 API,用于网络通信 |
|
||||
| **Endpoint** | - | 端点,API 的具体地址 |
|
||||
| **HTTP** | HyperText Transfer Protocol | Web API 使用的通信协议 |
|
||||
| **GET** | - | 获取资源的方法 |
|
||||
| **POST** | - | 提交数据的方法 |
|
||||
| **SDK** | Software Development Kit | 软件开发工具包,封装了底层 API 调用 |
|
||||
| **SDK** | Software Development Kit | 软件开发工具包,封装了底层 API 调用 |
|
||||
| **URL** | Uniform Resource Locator | API 的网络地址 |
|
||||
| **JSON** | JavaScript Object Notation | 常用的数据格式 |
|
||||
| **Authentication** | - | 验证身份的过程 |
|
||||
| **Status Code** | - | HTTP 响应中的状态码 |
|
||||
| **Request** | - | 请求 |
|
||||
| **Response** | - | 响应 |
|
||||
| **Header** | - | HTTP 头,包含元信息 |
|
||||
| **Header** | - | HTTP 头,包含元信息 |
|
||||
| **Payload** | - | 请求或响应的实际数据 |
|
||||
| **Rate Limit** | - | 速率限制 |
|
||||
| **Idempotent** | - | 幂等,多次执行结果相同 |
|
||||
| **Idempotent** | - | 幂等,多次执行结果相同 |
|
||||
| **REST** | Representational State Transfer | 一种 API 架构风格 |
|
||||
| **RPC** | Remote Procedure Call | 远程过程调用 |
|
||||
| **GraphQL** | - | 一种查询语言 API |
|
||||
| **gRPC** | - | Google 开发的高性能 RPC 框架 |
|
||||
|
||||
@@ -1,3 +1,297 @@
|
||||
# HTTP 协议
|
||||
# HTTP 协议:前后端的"通信语言"
|
||||
|
||||
> 待实现
|
||||
::: tip 🎯 核心问题
|
||||
**HTTP 是如何工作的?** 这就像问:两个人如何对话?需要约定语言、语法、对话规则。HTTP 就是前后端之间的"对话协议"。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 0. HTTP 的本质
|
||||
|
||||
**HTTP**(HyperText Transfer Protocol,超文本传输协议)是前后端通信的基础协议。
|
||||
|
||||
### 0.1 用对话来类比
|
||||
|
||||
| 对话要素 | HTTP 对应 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| 语言 | HTTP 协议 | 双方都能理解的语言 |
|
||||
| 语法 | 请求/响应格式 | 怎么"说话" |
|
||||
| 流程 | 请求-响应模式 | 一问一答 |
|
||||
| 结束 | 挂断 | TCP 连接关闭 |
|
||||
|
||||
---
|
||||
|
||||
## 1. HTTP 的发展历程
|
||||
|
||||
HTTP 从 1991 年诞生至今,经历了多次重大升级。
|
||||
|
||||
<HttpProtocolDemo />
|
||||
|
||||
### 1.1 版本对比
|
||||
|
||||
| 版本 | 年份 | 核心改进 | 典型特征 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **HTTP/0.9** | 1991 | 仅支持 GET | 纯文本,只有请求,无响应头 |
|
||||
| **HTTP/1.0** | 1996 | 增加 POST/HEAD | 每个请求一个 TCP 连接 |
|
||||
| **HTTP/1.1** | 1997 | 持久连接 | Keep-Alive,一个连接多个请求 |
|
||||
| **HTTP/2** | 2015 | 多路复用 | 二进制帧,头部压缩 |
|
||||
| **HTTP/3** | 2022 | 基于 QUIC | UDP 传输,解决队头阻塞 |
|
||||
|
||||
::: tip 💡 为什么需要 HTTP/2?
|
||||
HTTP/1.1 虽然支持持久连接,但请求必须串行发送(前一个请求的响应返回后,才能发送下一个请求)。HTTP/2 通过多路复用解决了这个问题,可以同时发送多个请求。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. HTTP 请求的结构
|
||||
|
||||
### 2.1 请求行
|
||||
|
||||
```http
|
||||
GET /api/users/123 HTTP/1.1
|
||||
```
|
||||
|
||||
包含三个部分:
|
||||
- **方法**:GET、POST、PUT、DELETE 等
|
||||
- **URL**:请求的资源路径
|
||||
- **版本**:HTTP/1.1 或 HTTP/2
|
||||
|
||||
### 2.2 请求头
|
||||
|
||||
```http
|
||||
Host: api.example.com
|
||||
User-Agent: Mozilla/5.0
|
||||
Accept: application/json
|
||||
Authorization: Bearer xxx
|
||||
Content-Type: application/json
|
||||
Content-Length: 45
|
||||
```
|
||||
|
||||
常见请求头:
|
||||
| 头部 | 说明 | 示例 |
|
||||
| :--- | :--- | :--- |
|
||||
| **Host** | 服务器域名 | `api.example.com` |
|
||||
| **User-Agent** | 客户端信息 | `Mozilla/5.0` |
|
||||
| **Accept** | 接受的响应类型 | `application/json` |
|
||||
| **Authorization** | 认证信息 | `Bearer token` |
|
||||
| **Content-Type** | 请求体类型 | `application/json` |
|
||||
|
||||
### 2.3 请求体
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "张三",
|
||||
"email": "zhangsan@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
只有 POST、PUT、PATCH 等方法才有请求体。
|
||||
|
||||
---
|
||||
|
||||
## 3. HTTP 响应的结构
|
||||
|
||||
### 3.1 状态行
|
||||
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
```
|
||||
|
||||
包含三个部分:
|
||||
- **版本**:HTTP/1.1
|
||||
- **状态码**:200、404、500 等
|
||||
- **状态文本**:OK、Not Found 等
|
||||
|
||||
### 3.2 响应头
|
||||
|
||||
```http
|
||||
Content-Type: application/json
|
||||
Content-Length: 156
|
||||
Cache-Control: max-age=3600
|
||||
Set-Cookie: session=xxx; HttpOnly
|
||||
```
|
||||
|
||||
常见响应头:
|
||||
| 头部 | 说明 | 示例 |
|
||||
| :--- | :--- | :--- |
|
||||
| **Content-Type** | 响应体类型 | `application/json` |
|
||||
| **Content-Length** | 响应体大小 | `156` |
|
||||
| **Cache-Control** | 缓存策略 | `max-age=3600` |
|
||||
| **Set-Cookie** | 设置 Cookie | `session=xxx` |
|
||||
|
||||
### 3.3 响应体
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"data": {
|
||||
"id": 123,
|
||||
"name": "张三"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. HTTP 方法详解
|
||||
|
||||
| 方法 | 用途 | 请求体 | 幂等性 | 安全性 |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **GET** | 获取资源 | 无 | 是 | 是 |
|
||||
| **POST** | 创建资源 | 有 | 否 | 否 |
|
||||
| **PUT** | 全量更新 | 有 | 是 | 否 |
|
||||
| **PATCH** | 部分更新 | 有 | 否 | 否 |
|
||||
| **DELETE** | 删除资源 | 无 | 是 | 否 |
|
||||
| **HEAD** | 获取头部 | 无 | 是 | 是 |
|
||||
| **OPTIONS** | 查询支持的方法 | 无 | 是 | 是 |
|
||||
|
||||
### 4.1 GET vs POST
|
||||
|
||||
| 特性 | GET | POST |
|
||||
| :--- | :--- | :--- |
|
||||
| **参数位置** | URL 查询参数 | 请求体 |
|
||||
| **缓存** | 可缓存 | 默认不缓存 |
|
||||
| **书签** | 可添加为书签 | 不可 |
|
||||
| **历史记录** | 保存在浏览器历史 | 不保存 |
|
||||
| **数据长度** | 有限制(URL 长度) | 无限制 |
|
||||
| **安全性** | 参数可见在 URL | 参数在请求体中 |
|
||||
|
||||
::: tip 💡 何时使用 GET/POST?
|
||||
- **GET**:查询、获取数据
|
||||
- **POST**:创建、提交数据
|
||||
- **PUT**:全量更新(替换整个资源)
|
||||
- **PATCH**:部分更新(只修改指定字段)
|
||||
- **DELETE**:删除资源
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. HTTP 状态码
|
||||
|
||||
### 5.1 状态码分类
|
||||
|
||||
| 分类 | 说明 | 典型状态码 |
|
||||
| :--- | :--- | :--- |
|
||||
| **2xx** | 成功 | 200 OK、201 Created、204 No Content |
|
||||
| **3xx** | 重定向 | 301 永久、302 临时、304 未修改 |
|
||||
| **4xx** | 客户端错误 | 400 参数错误、401 未认证、404 不存在 |
|
||||
| **5xx** | 服务端错误 | 500 内部错误、503 不可用 |
|
||||
|
||||
### 5.2 常用状态码
|
||||
|
||||
| 状态码 | 说明 | 使用场景 |
|
||||
| :--- | :--- | :--- |
|
||||
| **200 OK** | 请求成功 | GET、PUT 请求成功 |
|
||||
| **201 Created** | 创建成功 | POST 创建资源成功 |
|
||||
| **204 No Content** | 无内容 | DELETE 删除成功 |
|
||||
| **301 Moved Permanently** | 永久重定向 | URL 永久变更 |
|
||||
| **302 Found** | 临时重定向 | URL 临时变更 |
|
||||
| **304 Not Modified** | 未修改 | 缓存有效 |
|
||||
| **400 Bad Request** | 参数错误 | 请求参数格式错误 |
|
||||
| **401 Unauthorized** | 未认证 | 需要登录 |
|
||||
| **403 Forbidden** | 无权限 | 已登录但权限不足 |
|
||||
| **404 Not Found** | 不存在 | 资源不存在 |
|
||||
| **500 Internal Server Error** | 内部错误 | 服务器异常 |
|
||||
| **503 Service Unavailable** | 不可用 | 服务器维护或过载 |
|
||||
|
||||
---
|
||||
|
||||
## 6. HTTPS:安全的 HTTP
|
||||
|
||||
### 6.1 HTTP vs HTTPS
|
||||
|
||||
| 特性 | HTTP | HTTPS |
|
||||
| :--- | :--- | :--- |
|
||||
| **协议** | TCP | TCP + SSL/TLS |
|
||||
| **端口** | 80 | 443 |
|
||||
| **数据** | 明文传输 | 加密传输 |
|
||||
| **证书** | 不需要 | 需要 SSL 证书 |
|
||||
| **性能** | 略快 | 略慢(握手开销) |
|
||||
| **SEO** | 无影响 | 搜索引擎优先收录 |
|
||||
|
||||
### 6.2 HTTPS 的工作流程
|
||||
|
||||
1. **Client Hello**:客户端发送支持的加密套件
|
||||
2. **Server Hello**:服务器返回证书和选定的加密套件
|
||||
3. **验证证书**:客户端验证服务器证书的有效性
|
||||
4. **密钥交换**:使用非对称加密交换会话密钥
|
||||
5. **加密通信**:使用会话密钥进行对称加密通信
|
||||
|
||||
::: tip 💡 HTTPS 的优势
|
||||
- **防窃听**:数据加密,第三方无法读取
|
||||
- **防篡改**:数据完整性校验
|
||||
- **防冒充**:SSL 证书验证服务器身份
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 7. HTTP 缓存机制
|
||||
|
||||
### 7.1 缓存头
|
||||
|
||||
| 头部 | 说明 | 示例 |
|
||||
| :--- | :--- | :--- |
|
||||
| **Cache-Control** | 缓存策略 | `max-age=3600` |
|
||||
| **ETag** | 资源版本号 | `"33a64df551425fcc"` |
|
||||
| **Last-Modified** | 最后修改时间 | `Wed, 21 Oct 2015 07:28:00 GMT` |
|
||||
|
||||
### 7.2 缓存策略
|
||||
|
||||
**强缓存**:
|
||||
```http
|
||||
Cache-Control: max-age=3600
|
||||
```
|
||||
在 3600 秒内,浏览器直接使用缓存,不发送请求。
|
||||
|
||||
**协商缓存**:
|
||||
```http
|
||||
ETag: "33a64df551425fcc"
|
||||
```
|
||||
浏览器发送 `If-None-Match`,服务器返回 304(未修改)或 200(已修改)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 常见问题
|
||||
|
||||
### 8.1 GET 和 POST 的本质区别
|
||||
|
||||
**误区**:GET 和 POST 的区别只是参数位置不同。
|
||||
|
||||
**真相**:
|
||||
- GET 是幂等的,多次请求结果相同
|
||||
- POST 是非幂等的,多次请求可能创建多个资源
|
||||
- GET 可被缓存,POST 默认不缓存
|
||||
- GET 可被书签保存,POST 不可
|
||||
|
||||
### 8.2 HTTP/1.1 的队头阻塞
|
||||
|
||||
**问题**:HTTP/1.1 虽然支持持久连接,但请求必须串行发送。前一个请求响应慢,后续请求都要等待。
|
||||
|
||||
**解决方案**:
|
||||
- HTTP/2 多路复用
|
||||
- 域名分片(多个域名建立多个连接)
|
||||
- 连接池(限制并发数)
|
||||
|
||||
### 8.3 HTTP/2 的优势
|
||||
|
||||
| 特性 | HTTP/1.1 | HTTP/2 |
|
||||
| :--- | :--- | :--- |
|
||||
| **传输格式** | 文本 | 二进制帧 |
|
||||
| **多路复用** | 不支持 | 支持 |
|
||||
| **头部压缩** | 无 | HPACK 算法 |
|
||||
| **服务器推送** | 不支持 | 支持 |
|
||||
|
||||
---
|
||||
|
||||
## 名词速查表
|
||||
|
||||
| 名词 | 英文 | 解释 |
|
||||
| :--- | :--- | :--- |
|
||||
| **HTTP** | HyperText Transfer Protocol | 超文本传输协议 |
|
||||
| **HTTPS** | HTTP Secure | HTTP + SSL/TLS |
|
||||
| **TCP** | Transmission Control Protocol | 传输控制协议 |
|
||||
| **SSL/TLS** | Secure Sockets Layer | 安全套接层 |
|
||||
| **幂等性** | Idempotent | 多次请求结果相同 |
|
||||
| **持久连接** | Keep-Alive | 一个 TCP 连接发送多个请求 |
|
||||
| **多路复用** | Multiplexing | 同时发送多个请求 |
|
||||
| **队头阻塞** | Head-of-Line Blocking | 前面的请求阻塞后面的请求 |
|
||||
|
||||
@@ -1,3 +1,455 @@
|
||||
# 序列化与数据格式
|
||||
# 序列化:数据的"翻译"
|
||||
|
||||
> 待实现
|
||||
::: tip 🎯 核心问题
|
||||
**数据如何在网络上传输?** 这就像问:一个人说的话,如何让另一个人听懂?序列化解决的就是"数据翻译"的问题——把内存中的对象翻译成可以传输的格式。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 序列化数据的必要性
|
||||
|
||||
在前后端交互过程中,数据需要经历多次"变形"才能从服务器传递到客户端。
|
||||
|
||||
**场景一:前端收到的数据"变了"**
|
||||
|
||||
```javascript
|
||||
// 后端发送
|
||||
Date birth = new Date(1990, 5, 15)
|
||||
|
||||
// 前端收到
|
||||
{ "birth": "1990-06-15T00:00:00Z" } // 字符串!
|
||||
```
|
||||
|
||||
前端想用 `.getFullYear()`,结果报错了——因为这不是 Date 对象,是字符串。
|
||||
|
||||
**场景二:中文乱码**
|
||||
|
||||
```json
|
||||
// 期望
|
||||
{ "name": "张三" }
|
||||
|
||||
// 实际收到
|
||||
{ "name": "å¼ ä¸" }
|
||||
```
|
||||
|
||||
字符编码问题导致中文变成乱码。
|
||||
|
||||
**场景三:性能瓶颈**
|
||||
|
||||
```json
|
||||
// 一个包含 10000 条商品列表的响应
|
||||
{
|
||||
"products": [
|
||||
{ "id": 1, "name": "...", "description": "...", ... },
|
||||
// ... 9999 more
|
||||
]
|
||||
}
|
||||
// 大小:5.2 MB,传输时间:3.5 秒
|
||||
```
|
||||
|
||||
JSON 格式的冗余导致数据包太大,严重影响性能。
|
||||
|
||||
---
|
||||
|
||||
**序列化就像"翻译"**——把内存对象"翻译"成可以传输的格式,接收方再"翻译"回去。
|
||||
|
||||
---
|
||||
|
||||
## 1. 什么是序列化/反序列化?
|
||||
|
||||
**序列化**(Serialization)就是把对象转换成可传输格式的过程。
|
||||
|
||||
**反序列化**(Deserialization)就是把传输格式还原成对象的过程。
|
||||
|
||||
### 1.1 用寄快递来类比
|
||||
|
||||
| 寄快递 | 序列化 | 说明 |
|
||||
| :--- | :--- | :--- |
|
||||
| 打包物品 | 序列化 | 把物品装箱,贴上标签 |
|
||||
| 运输 | 网络传输 | 快递车运送到目的地 |
|
||||
| 拆包取物 | 反序列化 | 收件人打开箱子,取出物品 |
|
||||
|
||||
### 1.2 为什么需要序列化?
|
||||
|
||||
| 原因 | 说明 | 示例 |
|
||||
| :--- | :--- | :--- |
|
||||
| **网络传输** | 网络只能传输字节流 | API 调用、RPC 通信 |
|
||||
| **持久化存储** | 磁盘只能存储字节 | 保存对象到文件、数据库 |
|
||||
| **跨语言** | 不同语言的数据结构不同 | Java 对象 → Python 字典 |
|
||||
| **分布式缓存** | Redis/Memcached 存储字节 | 缓存用户信息 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 常见的序列化格式
|
||||
|
||||
👇 **动手试试看**:点击下方按钮,观察不同语言的序列化过程:
|
||||
|
||||
<SerializationDemo />
|
||||
|
||||
### 2.1 JSON:最通用
|
||||
|
||||
**优点**:
|
||||
- 可读性好,调试方便
|
||||
- 所有语言都支持
|
||||
- 浏览器原生支持(`JSON.parse` / `JSON.stringify`)
|
||||
|
||||
**缺点**:
|
||||
- 体积大(有大量 `{}` `""` 标记)
|
||||
- 不支持丰富的数据类型(Date、Map、Set 会被转换成字符串)
|
||||
|
||||
**适用场景**:
|
||||
- 公开 API
|
||||
- 前后端通信
|
||||
- 配置文件
|
||||
|
||||
### 2.2 XML:曾经的主流
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<user>
|
||||
<id>123</id>
|
||||
<name>张三</name>
|
||||
<email>zhangsan@example.com</email>
|
||||
<age>28</age>
|
||||
</user>
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 结构清晰,支持注释
|
||||
- 支持复杂的嵌套结构
|
||||
- 有 Schema 验证(XSD)
|
||||
|
||||
**缺点**:
|
||||
- 体积大,解析慢
|
||||
- 标签冗余(`<open></close>`)
|
||||
|
||||
**适用场景**:
|
||||
- 配置文件(Spring、MyBatis)
|
||||
- SOAP 协议
|
||||
- 复杂数据交换
|
||||
|
||||
### 2.3 Protobuf:最高效
|
||||
|
||||
```protobuf
|
||||
// user.proto
|
||||
syntax = "proto3";
|
||||
message User {
|
||||
int32 id = 1;
|
||||
string name = 2;
|
||||
string email = 3;
|
||||
int32 age = 4;
|
||||
}
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 体积小(比 JSON 小 30-50%)
|
||||
- 速度快(解析速度快 5-10 倍)
|
||||
- 向后兼容(新增字段不影响老版本)
|
||||
|
||||
**缺点**:
|
||||
- 不可读(二进制格式)
|
||||
- 需要 .proto 文件定义
|
||||
- 不支持动态类型
|
||||
|
||||
**适用场景**:
|
||||
- 微服务内部通信
|
||||
- 高性能场景(游戏、实时通信)
|
||||
- 移动端 App(节省流量)
|
||||
|
||||
### 2.4 MessagePack:兼顾可读性和性能
|
||||
|
||||
```json
|
||||
// MessagePack 是 JSON 的二进制版本
|
||||
// 相同数据,MessagePack 比 JSON 小 30% 左右
|
||||
```
|
||||
|
||||
**优点**:
|
||||
- 比 JSON 小,比 JSON 快
|
||||
- 保持 JSON 的数据模型
|
||||
- 支持所有 JSON 类型
|
||||
|
||||
**缺点**:
|
||||
- 不可读
|
||||
- 不如 Protobuf 高效
|
||||
|
||||
**适用场景**:
|
||||
- 需要性能但不想用 Protobuf
|
||||
- Redis 缓存
|
||||
- WebSocket 消息
|
||||
|
||||
---
|
||||
|
||||
## 3. 各语言序列化方式对比
|
||||
|
||||
| 语言 | JSON 库 | Protobuf 库 | XML 库 |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **JavaScript** | `JSON.stringify()` | `protobuf.js` | `fast-xml-parser` |
|
||||
| **Python** | `json.dumps()` | `protobuf` | `xmltodict` |
|
||||
| **Java** | `Jackson` / `Gson` | `protobuf-java` | `JAXB` |
|
||||
| **Go** | `encoding/json` | `proto` | `encoding/xml` |
|
||||
| **C++** | `nlohmann/json` | `protobuf` | `tinyxml2` |
|
||||
| **C#** | `System.Text.Json` | `Google.Protobuf` | `System.Xml` |
|
||||
|
||||
::: tip 💡 选择建议
|
||||
- **前后端通信**:JSON(调试方便)
|
||||
- **微服务内部**:Protobuf(性能最优)
|
||||
- **配置文件**:JSON 或 YAML
|
||||
- **旧系统对接**:XML(可能别无选择)
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 4. 性能对比
|
||||
|
||||
### 4.1 大小对比(以用户对象为例)
|
||||
|
||||
| 格式 | 大小 | 相对 JSON |
|
||||
| :--- | :--- | :--- |
|
||||
| JSON | 68 bytes | 100% |
|
||||
| XML | 142 bytes | 209% |
|
||||
| Protobuf | 38 bytes | 56% |
|
||||
| MessagePack | 52 bytes | 76% |
|
||||
|
||||
### 4.2 速度对比(序列化 10000 次)
|
||||
|
||||
| 格式 | 耗时 | 相对 JSON |
|
||||
| :--- | :--- | :--- |
|
||||
| JSON | 45 ms | 100% |
|
||||
| XML | 120 ms | 267% |
|
||||
| Protobuf | 8 ms | 18% |
|
||||
| MessagePack | 28 ms | 62% |
|
||||
|
||||
::: tip 💡 性能测试结论
|
||||
- **Protobuf 最快**:适合高性能场景
|
||||
- **MessagePack 次之**:比 JSON 快 40% 左右
|
||||
- **JSON 最慢**:但对大多数场景已经足够
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 常见问题
|
||||
|
||||
### 5.1 日期序列化问题
|
||||
|
||||
**问题**:Date 对象序列化后变成字符串
|
||||
|
||||
```javascript
|
||||
// 序列化前
|
||||
const date = new Date('2024-01-01')
|
||||
|
||||
// 序列化后
|
||||
JSON.stringify(date) // "2024-01-01T00:00:00.000Z"
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```javascript
|
||||
// 方案1:转成时间戳
|
||||
{ createdAt: date.getTime() } // 1704067200000
|
||||
|
||||
// 方案2:转成 ISO 字符串
|
||||
{ createdAt: date.toISOString() } // "2024-01-01T00:00:00.000Z"
|
||||
|
||||
// 方案3:自定义序列化
|
||||
JSON.stringify(obj, (key, value) => {
|
||||
if (value instanceof Date) {
|
||||
return { __type: 'Date', value: value.toISOString() }
|
||||
}
|
||||
return value
|
||||
})
|
||||
```
|
||||
|
||||
### 5.2 循环引用问题
|
||||
|
||||
**问题**:对象循环引用会报错
|
||||
|
||||
```javascript
|
||||
const obj = { name: 'test' }
|
||||
obj.self = obj
|
||||
JSON.stringify(obj) // TypeError: Converting circular structure to JSON
|
||||
```
|
||||
|
||||
**解决方案**:
|
||||
```javascript
|
||||
// 方案1:过滤掉循环引用
|
||||
const seen = new WeakSet()
|
||||
JSON.stringify(obj, (key, value) => {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (seen.has(value)) return
|
||||
seen.add(value)
|
||||
}
|
||||
return value
|
||||
})
|
||||
|
||||
// 方案2:使用 flatted 库
|
||||
import { parse, stringify } from 'flatted'
|
||||
stringify(obj) // 自动处理循环引用
|
||||
```
|
||||
|
||||
### 5.3 中文乱码问题
|
||||
|
||||
**问题**:中文序列化后乱码
|
||||
|
||||
**原因**:
|
||||
- 字符编码不一致(UTF-8 vs GBK)
|
||||
- BOM 标记
|
||||
|
||||
**解决方案**:
|
||||
```python
|
||||
# Python 确保使用 UTF-8
|
||||
import json
|
||||
json.dumps(data, ensure_ascii=False) # 不转义中文
|
||||
```
|
||||
|
||||
```javascript
|
||||
// Node.js 设置响应头
|
||||
res.setHeader('Content-Type', 'application/json; charset=utf-8')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 实战:电商系统序列化方案
|
||||
|
||||
### 6.1 场景分析
|
||||
|
||||
| 场景 | 格式选择 | 理由 |
|
||||
| :--- | :--- | :--- |
|
||||
| **App → 后端 API** | JSON | 调试方便,前后端统一 |
|
||||
| **后端 → 后端 RPC** | Protobuf | 性能最优,节省流量 |
|
||||
| **缓存到 Redis** | MessagePack | 比 JSON 小,可序列化复杂对象 |
|
||||
| **日志记录** | JSON | 便于日志分析工具解析 |
|
||||
|
||||
### 6.2 代码示例
|
||||
|
||||
```javascript
|
||||
// API 响应(JSON)
|
||||
app.get('/api/products/:id', async (req, res) => {
|
||||
const product = await db.getProduct(req.params.id)
|
||||
res.json({
|
||||
code: 0,
|
||||
data: product
|
||||
})
|
||||
})
|
||||
|
||||
// 微服务通信(Protobuf)
|
||||
// product.proto
|
||||
syntax = "proto3";
|
||||
message Product {
|
||||
int32 id = 1;
|
||||
string name = 2;
|
||||
int32 price = 3;
|
||||
}
|
||||
|
||||
// 服务端
|
||||
const proto = require('./product.proto')
|
||||
const message = proto.Product.create(product)
|
||||
const buffer = proto.Product.encode(message).finish()
|
||||
|
||||
// 客户端
|
||||
const decoded = proto.Product.decode(buffer)
|
||||
|
||||
// Redis 缓存(MessagePack)
|
||||
const msgpack = require('msgpack-lite')
|
||||
await redis.set(
|
||||
`product:${id}`,
|
||||
msgpack.encode(product)
|
||||
)
|
||||
const cached = msgpack.decode(await redis.get(`product:${id}`))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 用 AI 辅助选择序列化方案
|
||||
|
||||
AI 可以帮助你根据场景选择合适的序列化格式。
|
||||
|
||||
### 7.1 提示词模板
|
||||
|
||||
```
|
||||
你是一位资深的系统架构师,精通数据序列化技术。请帮我选择合适的序列化方案。
|
||||
|
||||
## 业务场景
|
||||
[描述你的场景,例如:电商 App、游戏后端、微服务等]
|
||||
|
||||
## 技术要求
|
||||
[列出约束条件,例如:
|
||||
- 前后端分离(Vue + Node.js)
|
||||
- 性能要求高(QPS > 10000)
|
||||
- 流量敏感(移动端,需节省流量)
|
||||
- 需要跨语言(Java + Python + Go)]
|
||||
|
||||
## 数据特征
|
||||
[描述数据特点,例如:
|
||||
- 数据量大(单次响应 > 1MB)
|
||||
- 结构复杂(多层嵌套)
|
||||
- 包含日期、二进制数据等特殊类型]
|
||||
|
||||
## 输出格式
|
||||
请按以下格式输出:
|
||||
1. 推荐方案(JSON/Protobuf/MessagePack/XML)
|
||||
2. 理由说明
|
||||
3. 性能对比(估算大小和速度)
|
||||
4. 代码示例
|
||||
```
|
||||
|
||||
### 7.2 实战示例
|
||||
|
||||
**输入提示词**:
|
||||
|
||||
```
|
||||
你是一位资深的系统架构师。我们的电商 App 需要优化商品列表接口性能。
|
||||
|
||||
## 业务场景
|
||||
- 电商 App 的商品列表接口
|
||||
- 单次返回 100 个商品
|
||||
- 每个 商品包含:id, name, price, image, description 等 20 个字段
|
||||
- 当前使用 JSON,响应大小约 200KB,耗时 800ms
|
||||
|
||||
## 技术要求
|
||||
- 前端是 Vue.js
|
||||
- 后端是 Node.js + Go 微服务
|
||||
- 需要兼顾性能和开发效率
|
||||
|
||||
## 优化目标
|
||||
- 响应时间降到 300ms 以内
|
||||
- 数据包大小减少 30%
|
||||
```
|
||||
|
||||
**AI 输出**:
|
||||
|
||||
```
|
||||
推荐方案:混合方案
|
||||
|
||||
1. **API 层**:继续使用 JSON(前端友好)
|
||||
2. **微服务通信**:切换到 Protobuf(性能提升)
|
||||
3. **静态数据**:启用 gzip 压缩
|
||||
|
||||
### 理由说明
|
||||
- JSON 对前端最友好,无需额外解析
|
||||
- 后端微服务用 Protobuf,可以减少 40% 的数据量
|
||||
- gzip 压缩对文本效果明显(JSON 可压缩 70%)
|
||||
|
||||
### 性能对比
|
||||
| 方案 | 大小 | 耗时 |
|
||||
|------|------|------|
|
||||
| 当前 (JSON) | 200 KB | 800 ms |
|
||||
| + gzip | 60 KB | 350 ms |
|
||||
| + Protobuf | 50 KB | 280 ms |
|
||||
|
||||
### 代码示例
|
||||
[具体实现代码...]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 名词速查表
|
||||
|
||||
| 名词 | 英文 | 解释 |
|
||||
| :--- | :--- | :--- |
|
||||
| **序列化** | Serialization | 对象 → 字节流 |
|
||||
| **反序列化** | Deserialization | 字节流 → 对象 |
|
||||
| **JSON** | JavaScript Object Notation | 最常用的文本格式 |
|
||||
| **XML** | Extensible Markup Language | 标记语言,曾主流 |
|
||||
| **Protobuf** | Protocol Buffers | Google 开源的高效格式 |
|
||||
| **MessagePack** | - | JSON 的二进制版本 |
|
||||
| **编码** | Encoding | 字符 → 字节 |
|
||||
| **解码** | Decoding | 字节 → 字符 |
|
||||
|
||||
Reference in New Issue
Block a user