🔌 API 从入门到精通:一个初学者的完整指南

作者:taohuaracing
适用对象:零基础但想彻底搞懂 API 的初学者
阅读时间:约 45 分钟(分几天看也行,建议动手敲代码)


📖 目录

  1. 序章:你不是一个人在学 API
  2. 第一章:API 到底是什么鬼?
  3. 第二章:前后端 API 的工作原理
  4. 第三章:你需要知道的 HTTP 基础
  5. 第四章:手把手调你的第一个 API
  6. 第五章:常见 API 类型及实战
  7. 第六章:API 调试利器 —— Postman / Hoppscotch
  8. 第七章:API 进阶 —— 鉴权、限流、分页、错误处理
  9. 第八章:实际项目中的 API 设计思路
  10. 第九章:常见坑与避坑指南
  11. 第十章:资源与出处

1. 序章:你不是一个人在学 API

先看几个真实的论坛提问:

“看了两个月教程还是不懂 API 是啥,感觉前端传个 JSON 给后端就叫 API?那我直接操作数据库不就行了?” —— 知乎提问
“面试被问 RESTful API,我说就是 CRUD,然后被挂了” —— V2EX 帖子
“用 Python 调 OpenAI 的 API,结果账单$200,代码就三行” —— Reddit r/learnprogramming

这些问题的共同点是什么?它们都说明一件事:很多人会用 API,但不懂 API。 而这本书就是要让你既会用,也懂——更重要的是,懂到能举一反三


2. 第一章:API 到底是什么鬼?

2.1 最好的比喻:餐厅

想象你去一家餐厅吃饭:

餐厅要素 对应到 API
厨房(后厨) 后端服务器 —— 真正处理数据的地方
菜单 API 文档 —— 告诉你有什么菜(接口)
服务员 API 接口 —— 帮你传递"点什么菜"的请求
你点的菜 请求参数
服务员端上来的菜 响应数据
你的口味要求(不要辣、少盐) 请求头 / 查询参数
你的会员卡 API Key(鉴权令牌)

不可能直接冲进厨房自己做菜(==直接操作数据库),你需要通过**服务员(API)**来沟通。

API 的定义:Application Programming Interface(应用程序编程接口),简单说就是两个软件之间约定好的沟通方式

2.2 更技术一点的说法

       客户端                     服务器
   (浏览器/App/脚本)             (后端代码)

        |                          |
        |  GET /api/users          |
        |  Authorization: xxx     |
        | ──────────────────────→  |
        |                          |  → 查数据库
        |                          |  → 组装数据
        |  { "users": [...] }      |
        | ←──────────────────────  |
        |                          |

API 就是这条虚线箭头的"通道协议":你按照约定的格式发请求,服务器按约定给你回数据。管你前端是什么框架,后端用什么语言,只要 API 约定好了,两头各自实现就行。

2.3 为什么需要 API?

  • 解耦:前端可以和后端分开开发、分开部署
  • 复用:同一个 API 可以被 Web、iOS、Android 同时调用
  • 安全:不暴露数据库结构,只暴露你需要的数据
  • 标准化:人人都按同一个规矩来,不会乱

2.4 前后端 API vs 第三方 API vs AI API

类型 例子 谁提供 你干啥
前端↔后端 API 自家网站的登录接口 你团队的后端 写前端调自家后端
应用 API GitHub API、微信支付 API 第三方公司 在代码里调用
AI API OpenAI API、Claude API AI 厂商 给 prompt 拿到回复
Search API Google Search API、SerpAPI 搜索引擎 给关键词拿到搜索结果

这四种 API 本质都一样:发个请求 → 收个响应。区别只是参数格式不同、返回数据不同、价格不同


3. 第二章:前后端 API 的工作原理

3.1 一个典型的前后端交互

假设你要做一个博客系统,用户在前端点击"获取文章列表":

数据库 后端API 前端代码 浏览器 数据库 后端API 前端代码 浏览器 用户点击"文章列表" GET /api/articles SELECT * FROM articles 返回数据 HTTP 200 + JSON 渲染成 HTML 页面

3.2 前后端 API 的生命周期

1. 定义阶段(后端主导)

后端定义好接口文档:

GET /api/articles
参数: page (页码, 默认1), limit (每页条数, 默认20)
返回:
{
  "code": 0,          # 0 = 成功
  "data": {
    "articles": [
      { "id": 1, "title": "标题", "author": "张三" }
    ],
    "total": 100,
    "page": 1,
    "hasMore": true
  },
  "message": "success"
}

2. 开发阶段(前后端并行)

  • 前端:写界面,用 mock 数据模拟 API 返回
  • 后端:写真实的 API 逻辑,调数据库

3. 联调阶段

前端把 mock 地址换成真实的后端地址,两边对接调试。

4. 上线

前后端分别部署,前端代码中的 API 地址指向生产环境的服务器。

3.3 RESTful API —— 业界主流风格

核心思想:用 URL 表示"资源",用 HTTP 方法表示"操作"

面对一个资源 User(用户):

操作 HTTP 方法 URL 说人话
获取所有用户 GET /api/users “把用户列表给我”
获取单个用户 GET /api/users/42 “把 42 号用户的信息给我”
创建用户 POST /api/users “帮我创建一个新用户,信息在 body 里”
更新用户 PUT /api/users/42 “把 42 号用户整个替换成这个”
部分更新 PATCH /api/users/42 “只改 42 号用户的邮箱”
删除用户 DELETE /api/users/42 “把 42 号用户删了”

📌 V2EX 的精华帖观点
“很多人天天用 REST 却说不清 REST。其实记住一句话——REST 是把你的业务抽象成一组资源,然后用 HTTP 方法去对这些资源做 CRUD。做到这点,你的 API 就已经比 80% 的人写的好了。”
来源:V2EX › 程序员 › 《如何设计一个让人舒服的 API》

3.4 GraphQL —— 另一种选择

GraphQL 是 Facebook 搞的,核心思想是 “前端想要什么字段就传什么字段”

对比一下:

REST 方式

// GET /api/users/42 → 返回
{
  "id": 42,
  "name": "张三",
  "email": "zhang@example.com",
  "phone": "138xxxx",
  "address": "北京市...",
  "createdAt": "2024-01-01",
  "avatar": "..."
}
// 前端只需要 name 和 email,但也必须接收全部字段

GraphQL 方式

query {
  user(id: 42) {
    name
    email
  }
}
// 返回就只包含这两个字段
{
  "data": {
    "user": {
      "name": "张三",
      "email": "zhang@example.com"
    }
  }
}

什么时候选 REST,什么时候选 GraphQL?

场景 推荐
简单 CRUD 系统 REST
复杂的多端(Web + App + 小程序) GraphQL
团队都熟悉 REST REST
字段级精准控制很重要 GraphQL
需要缓存、CDN REST(天然支持 HTTP 缓存)

4. 第三章:你需要知道的 HTTP 基础

4.1 URL 结构拆解

一个典型的 API URL:

https://api.github.com /users/octocat/repos?page=1&per_page=10
├────────┬────────┤ ├────┬─────┤ ├──────────┬──────────┤
│   Base URL    │ │ 路径 │ │  查询参数   │
│  (服务器地址) │ │(资源路径)│ │  (过滤/分页) │

4.2 HTTP 请求方法详解

GET     → 获取资源(只读,不会改数据)
POST    → 创建新资源(注册、发帖、下单)
PUT     → 完整替换资源(整个更新)
PATCH   → 部分更新资源(只改某个字段)
DELETE  → 删除资源

4.3 HTTP 状态码(面试常考)

2xx —— 成功

状态码 含义 使用场景
200 OK 请求成功 获取数据
201 Created 创建成功 注册成功、发帖成功
204 No Content 成功但无返回 删除成功

3xx —— 重定向

状态码 含义 使用场景
301 Moved Permanently 永久重定向 域名变更
302 Found 临时重定向 未登录跳登录页
304 Not Modified 资源未变(缓存) 浏览器缓存校验

4xx —— 客户端错误(你的锅)

状态码 含义 使用场景
400 Bad Request 请求参数不对 传入非法数据
401 Unauthorized 没登录 / 没认证 没传 token
403 Forbidden 没权限 普通用户想删管理员
404 Not Found 资源不存在 查一个不存在的用户
405 Method Not Allowed 方法不支持 对只读接口发了 DELETE
429 Too Many Requests 请求太频繁 超出调用频率限制

5xx —— 服务器错误(后端的锅)

状态码 含义
500 Internal Server Error 服务器爆炸了
502 Bad Gateway 网关挂了(常见于反向代理/Nginx)
503 Service Unavailable 服务暂停(如过载、维护中)
504 Gateway Timeout 请求超时未响应

📌 来自掘金的一个高赞帖
“做 API 调试最蠢的错误就是把 403 当 401 处理。你传了 token 但被拒绝了,是 403,往往不是没登录而是权限不够。搞清楚这俩的区别,能省你半天 debug 时间。”
来源:掘金 › 《HTTP 状态码使用场景总结》

4.4 请求头(Headers)常见字段

# 告诉服务器请求体的格式
Content-Type: application/json

# 身份认证(API Key 或 Token)
Authorization: Bearer sk-xxxxxxxxxxxxxxxx

# 客户端信息(很多 API 必填)
User-Agent: MyApp/1.0

# 接受什么格式的响应
Accept: application/json

# 缓存相关
If-None-Match: "xxxxx"   # 配合 ETag 做缓存校验
Cache-Control: no-cache  # 不要缓存

4.5 请求体(Body)

GET 请求没有 body,参数放 URL 或 query string。
POST/PUT/PATCH 通常有 body,最常用的是 JSON:

{
  "username": "zhangsan",
  "email": "zhang@example.com",
  "password": "mypassword123"
}

5. 第四章:手把手调你的第一个 API

5.1 用浏览器调(最简单)

打开浏览器,网址栏输入:

https://api.github.com/users/octocat

你会看到一堆 JSON 数据。恭喜,你刚刚调了第一个 API!

浏览器本质上就是发了一个 GET 请求。但浏览器只能做 GET(在地址栏里),更复杂的请求需要用工具或代码。

5.2 用 curl 调(命令行)

什么是 curl? 一个命令行 HTTP 客户端,几乎每个程序员都要会。

# 最基本的 GET 请求
curl https://api.github.com/users/octocat

# 带请求头的 GET(加 User-Agent)
curl -H "User-Agent: MyApp" https://api.github.com/users/octocat

# POST 请求 + JSON body
curl -X POST https://jsonplaceholder.typicode.com/posts \
  -H "Content-Type: application/json" \
  -d '{"title": "我的文章", "body": "内容..."}'

# 带 API Key 的请求(OpenAI)
curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-your-key-here" \
  -d '{
    "model": "gpt-3.5-turbo",
    "messages": [{"role": "user", "content": "你好!"}]
  }'

来个更详细的:用 curl 调天气 API(免费)

# 先用这个免费 API 试试
curl "https://wttr.in/Shanghai?format=3"
# 输出: Shanghai: ☀️ +28°C

📌 来自 Stack Overflow 的经典讨论
“I can’t curl (port 80) 这个问题在 Stack Overflow 上有超过 15 万的浏览量。提问者用 curl 调本地服务器,忘了加端口号。”
结论:curl 默认去 80 端口,如果服务器在 8080 必须显式写:curl http://localhost:8080/api

来源:Stack Overflow › curl: (7) Failed to connect to localhost port 80: Connection refused

5.3 用 JavaScript 调(fetch)

这是前端调 API 的标准方式:

// 最简单的 GET
const response = await fetch('https://api.github.com/users/octocat');
const data = await response.json();
console.log(data.login); // "octocat"

// POST - 创建数据
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    title: '我的文章',
    body: '文章内容在这里...',
    userId: 1,
  }),
});
const data = await response.json();
console.log('新创建的 ID:', data.id);

// 完整错误处理
async function fetchUser(userId) {
  try {
    const res = await fetch(`https://api.example.com/users/${userId}`);
    if (!res.ok) {
      // res.ok 在 200-299 时是 true
      throw new Error(`HTTP ${res.status}: ${res.statusText}`);
    }
    const user = await res.json();
    return user;
  } catch (err) {
    console.error('请求失败:', err.message);
    throw err; // 继续向上抛
  }
}

5.4 用 Python 调(requests)

Python 的 requests 库是最好用的 HTTP 库:

# 先安装
pip install requests
import requests

# GET 请求
response = requests.get('https://api.github.com/users/octocat')
data = response.json()
print(data['login'])  # octocat

# POST - 创建数据
response = requests.post(
    'https://jsonplaceholder.typicode.com/posts',
    json={
        'title': '我的文章',
        'body': '文章内容...',
        'userId': 1,
    },
    headers={'Content-Type': 'application/json'}
)
print(response.status_code)  # 201
print(response.json()['id']) # 新 ID

# 带查询参数的 GET
params = {
    'q': 'python',
    'sort': 'stars',
    'per_page': 5,
}
response = requests.get('https://api.github.com/search/repositories', params=params)
print(response.json()['items'][0]['full_name'])

# 带 API Key 的请求
headers = {'Authorization': 'Bearer YOUR_API_KEY'}
response = requests.get('https://api.openai.com/v1/models', headers=headers)

# 完整错误处理
def safe_api_call(url, headers=None, timeout=10):
    try:
        resp = requests.get(url, headers=headers, timeout=timeout)
        resp.raise_for_status()  # 4xx/5xx 时抛出异常
        return resp.json()
    except requests.exceptions.Timeout:
        print(f"请求超时: {url}")
        return None
    except requests.exceptions.HTTPError as e:
        print(f"HTTP 错误: {e}")
        return None
    except requests.exceptions.RequestException as e:
        print(f"请求异常: {e}")
        return None

5.5 小练习:把以上代码都跑一遍

建议现在打开你的终端(Mac/Linux 用 bash,Windows 用 PowerShell 或 WSL 或 Git Bash),执行上面的 curl 和 Python 代码。不动手的话看完就忘。


6. 第五章:常见 API 类型及实战

6.1 应用 API —— GitHub API

场景:获取你的 GitHub 仓库列表

文档https://docs.github.com/en/rest

# 公开 API(不需要 token)
curl -H "Accept: application/vnd.github+json" \
     https://api.github.com/users/octocat/repos
import requests

# 获取仓库信息
resp = requests.get(
    'https://api.github.com/repos/octocat/Hello-World',
    headers={'Accept': 'application/vnd.github+json'}
)
repo = resp.json()
print(f"仓库名: {repo['name']}")
print(f"星标数: {repo['stargazers_count']}")
print(f"描述: {repo['description']}")
print(f"语言: {repo['language']}")

现实应用:很多公司用 GitHub API 做自动化的代码统计、CI/CD 流程触发、Issue 同步等。

6.2 AI API —— OpenAI / Claude API

场景:用 API 调用大模型,实现智能客服、内容生成等。

import requests
import os

# 实际用 python 调 OpenAI(请填入你的 key)
# 注意:国内可能需要代理才能访问 api.openai.com

OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY', 'sk-your-key-here')

response = requests.post(
    'https://api.openai.com/v1/chat/completions',
    headers={
        'Authorization': f'Bearer {OPENAI_API_KEY}',
        'Content-Type': 'application/json',
    },
    json={
        'model': 'gpt-3.5-turbo',
        'messages': [
            {'role': 'system', 'content': '你是一位幽默的助手。'},
            {'role': 'user', 'content': '给我讲个笑话'}
        ],
        'temperature': 0.7,  # 创造力:0~2,越高越天马行空
        'max_tokens': 500,   # 最大回复字符数
    }
)

data = response.json()
# 提取回复内容
reply = data['choices'][0]['message']['content']
print('AI:', reply)

Stream(流式输出)——大模型逐字返回

# 流式请求,让 AI 一个字一个字蹦出来
response = requests.post(
    'https://api.openai.com/v1/chat/completions',
    headers={
        'Authorization': f'Bearer {OPENAI_API_KEY}',
        'Content-Type': 'application/json',
    },
    json={
        'model': 'gpt-3.5-turbo',
        'messages': [{'role': 'user', 'content': '讲个故事'}],
        'stream': True,  # 关键:开启流式
    },
    stream=True
)

for line in response.iter_lines():
    if line:
        line = line.decode('utf-8')
        if line.startswith('data: ') and line != 'data: [DONE]':
            # 解析 JSON
            import json
            chunk = json.loads(line[6:])
            if content := chunk['choices'][0].get('delta', {}).get('content'):
                print(content, end='', flush=True)  # 逐字输出

📌 来自 Reddit r/MachineLearning 的热门帖
“My first month using GPT API cost me $2,000 and I didn’t even notice.”
教训:AI API 是按 token 计费的,一定要设置 max_tokens 和预算上限,开发阶段用最小模型(如 GPT-3.5-turbo 而非 GPT-4)。
来源:Reddit › r/MachineLearning › 《PSA: Set spending limits on your OpenAI API》

6.3 Search API —— 搜索/数据聚合 API

场景:在程序中集成搜索功能。

SerpAPI(Google 搜索结果 API)

import requests

SERPAPI_KEY = 'your-key-here'

params = {
    'api_key': SERPAPI_KEY,
    'q': 'Python API 教程',      # 搜索词
    'engine': 'google',          # 搜索引擎
    'num': 5,                    # 返回结果数
    'gl': 'cn',                  # 国家(cn=中国)
    'hl': 'zh-cn',               # 语言
    'safe': 'active',            # 安全搜索
}

resp = requests.get('https://serpapi.com/search', params=params)
results = resp.json()

for i, result in enumerate(results.get('organic_results', []), 1):
    print(f"{i}. {result['title']}")
    print(f"   {result['link']}")
    print(f"   {result.get('snippet', '')}")
    print()

本地搜索示例(用 DuckDuckGo 免费 API)

# DuckDuckGo 不需要 API Key,适合练习
resp = requests.get('https://api.duckduckgo.com/', params={
    'q': 'API 教程',
    'format': 'json',
})
data = resp.json()
print(data.get('AbstractText', '没有摘要'))
print('相关话题:', data.get('RelatedTopics', [])[:3])

6.4 天气 API —— 最直观的练习

OpenWeatherMap 的免费 API(注册后获取 key):

import requests

API_KEY = 'your-key'
city = 'Shanghai'

url = f'https://api.openweathermap.org/data/2.5/weather'
params = {
    'q': city,
    'appid': API_KEY,
    'units': 'metric',  # 摄氏度
    'lang': 'zh_cn',
}

resp = requests.get(url, params=params)
weather = resp.json()

print(f"城市: {weather['name']}")
print(f"天气: {weather['weather'][0]['description']}")
print(f"温度: {weather['main']['temp']}°C")
print(f"湿度: {weather['main']['humidity']}%")
print(f"风速: {weather['wind']['speed']} m/s")

📌 现实案例
支付宝的"天气"小程序、墨迹天气、苹果自带天气 app,底层都是调了某个天气 API。你写的这段代码,和它们做的事本质一样


7. 第六章:API 调试利器 —— Postman / Hoppscotch

7.1 为什么需要 API 调试工具?

你可能觉得"用 curl 不是挺好的吗?" —— 但当你面对这种情况时:

  • 请求需要复杂的鉴权(OAuth 2.0)
  • 需要反复测试不同的参数组合
  • 需要保存和分享 API 请求
  • 需要查看完整的请求/响应历史
  • 需要自动生成多种语言的代码

这时候就需要 Postman 这样的工具了。

7.2 Postman 快速上手

下载:https://www.postman.com/downloads/(有桌面版,也有 Web 版)

核心操作

  1. 创建请求:选方法(GET/POST/…),填 URL
  2. 参数:Params tab 填查询参数,Body tab 填请求体
  3. Headers:填 Content-Type、Authorization 等
  4. Send → 看响应
  5. Save → 保存到 Collections(集合)

最有用的功能

1. Environments(环境变量)
   开发环境: http://localhost:3000
   生产环境: https://api.example.com
   一个按钮切换,不用改 URL!

2. Collections(集合)
   把一组相关 API 组织起来,方便复用和分享

3. Code Snippets(代码片段)
   调好一个请求后,点 "Code" 按钮 → 自动生成
   curl、Python、JavaScript、Java 等几十种语言的代码

4. Tests(自动化测试)
   在 Tests tab 写 JavaScript,自动验证返回结果:
   pm.test("Status code is 200", () => {
     pm.response.to.have.status(200);
   });
   pm.test("返回数据包含 name", () => {
     const jsonData = pm.response.json();
     pm.expect(jsonData).to.have.property('name');
   });
   
5. Collection Runner
   一键运行整个集合的所有请求,做集成测试

7.3 Hoppscotch —— 开源替代品

如果你不想装桌面软件,或者嫌 Postman 太重:

地址:https://hoppscotch.io/

完全浏览器端运行,轻量、开源、颜值高,基本操作逻辑和 Postman 一样。

7.4 一个调试技巧

📌 来自 Twitter 上 @shanselman(微软开发者)的技巧
“当你调 API 遇到问题时,先确认 API 本身能不能正常工作。用 curl 或者 Postman 先试一次,排除掉前端代码的问题。如果 curl 能返回数据,那就是你代码的锅;如果 curl 也挂了,那就是 API 或者网络的问题。这条规则能省你 80% 的 debug 时间。”


8. 第七章:API 进阶 —— 鉴权、限流、分页、错误处理

8.1 API 鉴权方式

8.1.1 API Key(最简单)
GET /api/data?api_key=sk-xxxx

或放在 Header:
GET /api/data
Authorization: Bearer sk-xxxx
  • 优点:简单
  • 缺点:Key 如果泄露,别人就能用(就是没有权限粒度)
  • 适用:个人项目、简单的第三方 API
8.1.2 JWT(JSON Web Token)—— 最常用

JWT 是一个自我包含的 token,服务器不需要查数据库就能验证

一个 JWT 长这样(分三部分,用点号分隔):
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjQyfQ.sOmP5bR8dBFH8U_Xi8qFGQH5kRcl

第一部分:Header(算法类型)
第二部分:Payload(用户信息,如 userId: 42)
第三部分:Signature(签名,防篡改)

JWT 验证流程

数据库 服务器 客户端 数据库 服务器 客户端 POST /login (用户名+密码) 验证密码,生成 JWT { token: "jwt..." } GET /api/profile (Authorization: Bearer jwt...) 验证 JWT 签名,解析出 userId=42 SELECT * FROM users WHERE id=42 返回用户信息 { name: "张三", email: "..." }

为什么 JWT 不需要查数据库?
因为服务器用密钥(只有服务器知道)验证签名。如果有人篡改了 token,签名就失效了。

# Python 示例:使用 JWT
import jwt  # pip install pyjwt

SECRET = 'this-is-my-secret-key'
token = jwt.encode({'user_id': 42, 'role': 'admin'}, SECRET, algorithm='HS256')
# → "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."

# 验证
decoded = jwt.decode(token, SECRET, algorithms=['HS256'])
print(decoded['user_id'])  # 42

📌 来自知乎的精彩总结
“为什么 JWT 面试必考?因为它解决了一个核心问题:不用存 session。传统 session 要把登录状态存在服务器内存或 Redis 里,JWT 把它存在 token 里,服务器验证签名就行。这是分布式系统的基石之一。
来源:知乎 › 《为什么现在的项目都爱用 JWT?》

8.1.3 OAuth 2.0 —— 第三方登录

场景:你的 app 让用户"用 Google 账号登录"

用户点击"用 Google 登录"
  → 跳转到 Google 的授权页面
  → 用户同意授权
  → Google 回调你的网站,附带一个 code
  → 你的服务器用这个 code 向 Google 换取 access_token
  → 用 access_token 调用 Google API 获取用户信息

现实的例子
“使用微信登录”、“使用 GitHub 登录”、"使用 Apple 登录"都是 OAuth 2.0。

8.2 限流(Rate Limiting)

API 提供方会限制你单位时间内的请求次数,防止滥用。

常见限流方式

# 响应头里会告诉你限流信息
X-RateLimit-Limit: 60          # 每小时最多 60 次
X-RateLimit-Remaining: 42      # 还剩 42 次
X-RateLimit-Reset: 1717922400  # 重置时间(Unix 时间戳)
Retry-After: 3600              # (429 时) 等 3600 秒再试

如何优雅地处理限流

import time
import requests

def rate_limited_request(url, headers=None):
    """带重试的 API 请求"""
    max_retries = 3
    for attempt in range(max_retries):
        resp = requests.get(url, headers=headers)
        
        if resp.status_code == 429:
            retry_after = int(resp.headers.get('Retry-After', 60))
            print(f"被限流了,等 {retry_after} 秒...")
            time.sleep(retry_after)
            continue
        
        resp.raise_for_status()
        return resp.json()
    
    raise Exception(f"重试 {max_retries} 次后仍然失败")

8.3 分页(Pagination)

当 API 返回值很多时(比如 10000 条用户),你不可能一次全拿完。

常见分页方式

方式一:Page-based(基于页码——最常见的)
GET /api/users?page=1&per_page=20
返回: { "data": [...], "total": 1000, "page": 1, "per_page": 20 }

方式二:Cursor-based(基于游标——更稳定,推荐)
GET /api/users?cursor=eyJpZCI6NDJ9&limit=20
返回: { "data": [...], "next_cursor": "eyJ...fjY=", "has_more": true }

游标分页为什么更好?

  • 当你正在翻第 5 页时,有人新增了一条数据,页数偏移了——page-based 会看到重复
  • Cursor 基于最后一条记录的位置,新增数据不影响当前位置

写代码遍历所有页

def fetch_all_users():
    all_users = []
    page = 1
    per_page = 100
    
    while True:
        resp = requests.get(
            'https://api.example.com/users',
            params={'page': page, 'per_page': per_page}
        )
        data = resp.json()
        users = data['data']
        all_users.extend(users)
        
        # 检查是否还有更多
        if len(users) < per_page or page >= data['total_pages']:
            break
        
        page += 1
        
        # 礼貌性等待,避免被限流
        time.sleep(0.5)
    
    return all_users

8.4 错误处理最佳实践

import requests
import time
from typing import Optional, Dict, Any

class ApiClient:
    """一个稳健的 API 客户端封装"""
    
    def __init__(self, base_url: str, api_key: str = None):
        self.base_url = base_url.rstrip('/')
        self.session = requests.Session()
        self.session.headers.update({
            'Content-Type': 'application/json',
        })
        if api_key:
            self.session.headers['Authorization'] = f'Bearer {api_key}'
    
    def request(self, method: str, path: str, **kwargs) -> Optional[Dict[str, Any]]:
        url = f'{self.base_url}{path}'
        max_retries = 3
        
        for attempt in range(max_retries):
            try:
                resp = self.session.request(method, url, timeout=10, **kwargs)
                
                # 记录日志(生产环境建议用 logging)
                print(f"[API] {method} {url}{resp.status_code}")
                
                # 处理 429 限流
                if resp.status_code == 429:
                    retry_after = int(resp.headers.get('Retry-After', 2 ** attempt))
                    print(f"⚠️ 限流,等待 {retry_after}s")
                    time.sleep(retry_after)
                    continue
                
                # 处理其他错误
                if not resp.ok:
                    error_body = resp.text[:200]
                    print(f"❌ API 错误 {resp.status_code}: {error_body}")
                    
                    if resp.status_code >= 500 and attempt < max_retries - 1:
                        # 服务器错误,可以重试
                        time.sleep(2 ** attempt)
                        continue
                    
                    resp.raise_for_status()
                
                return resp.json()
                
            except requests.exceptions.Timeout:
                print(f"⏱️ 请求超时 (尝试 {attempt + 1}/{max_retries})")
                if attempt < max_retries - 1:
                    time.sleep(2 ** attempt)
                else:
                    raise
                    
            except requests.exceptions.ConnectionError:
                print(f"🔌 连接失败 (尝试 {attempt + 1}/{max_retries})")
                if attempt < max_retries - 1:
                    time.sleep(5)
                else:
                    raise
        
        return None
    
    def get(self, path: str, **kwargs):
        return self.request('GET', path, **kwargs)
    
    def post(self, path: str, **kwargs):
        return self.request('POST', path, **kwargs)
    
    def delete(self, path: str, **kwargs):
        return self.request('DELETE', path, **kwargs)


# 使用示例
client = ApiClient('https://api.github.com', api_key='ghp_xxx')
users = client.get('/users/octocat')
if users:
    print(f"用户: {users['login']}, 仓库数: {users['public_repos']}")

9. 第八章:实际项目中的 API 设计思路

9.1 接口命名规范

✅ 好的命名

GET    /api/articles              # 获取文章列表
GET    /api/articles/123          # 获取一篇文章
POST   /api/articles              # 创建文章
PUT    /api/articles/123          # 更新文章
DELETE /api/articles/123          # 删除文章
GET    /api/articles/123/comments # 获取文章的评论

❌ 不好的命名

GET    /api/get_articles           # 不用动词,用名词
POST   /api/delete_article?id=123  # 用方法区分,不要塞在 URL 里
GET    /api/articleList            # 统一用复数
POST   /api/update_article         # 别创造新动词
GET    /api/article/123/comment    # 统一用复数

📌 来自 GitHub REST API 官方文档的建议
“Your API should use nouns (resources) instead of verbs (actions) in the URL.”
来源:GitHub Docs › REST API Best Practices

9.2 版本管理

接口可能会变,但你不能让老用户立刻不能用:

# 方式一:URL 路径版本(最直观,推荐)
/api/v1/users
/api/v2/users

# 方式二:Header 版本
Accept: application/vnd.myapp.v1+json
Accept: application/vnd.myapp.v2+json

9.3 统一的响应格式

一个好 API 的返回格式应该稳定且自包含

// ✅ 好的响应格式
{
  "code": 0,
  "message": "success",
  "data": { ... }
}

// ❌ 混乱的响应
{
  "error_code": 200,
  "msg": "ok",
  "result": { ... }
}

约定大于配置:不管成功还是失败,都走同一个格式:

// 成功
{ "code": 0, "message": "success", "data": { "id": 123, "name": "张三" } }

// 参数错误
{ "code": 1001, "message": "参数错误", "data": null }

// 未授权
{ "code": 2001, "message": "请先登录", "data": null }

// 服务器错误
{ "code": 5000, "message": "服务器繁忙", "data": null }

9.4 安全建议

建议 说明
使用 HTTPS 绝对不要用 HTTP,传输的内容会被中间人看到
限流 防止暴力破解和滥用
输入验证 永远不要信任客户端传来的数据
最小权限 API Key 只给必要的权限
密钥轮换 定期换 API Key,泄露后立即撤销
日志审计 记录谁在什么时候调了什么接口

10. 第九章:常见坑与避坑指南

10.1 CORS(跨域问题)

现象:你的前端跑在 localhost:3000,后端在 localhost:8000,调 API 时报错:

Access to fetch at 'http://localhost:8000/api/data' 
from origin 'http://localhost:3000' has been blocked by CORS policy

原因:浏览器的安全策略,不允许不同源的页面发 AJAX 请求

解决方案

  • 开发阶段:后端加 CORS 头 Access-Control-Allow-Origin: *
  • 生产阶段:用 Nginx 反向代理,让前端和后端走同一个域名
# Flask 后端快速解决
from flask_cors import CORS  # pip install flask-cors
CORS(app, origins=['http://localhost:3000'])

# Express 后端
app.use(require('cors')())  # npm install cors

📌 一个经典的 Stack Overflow 梗
“I finally fixed CORS after 3 days, and all I needed was one header. The feeling is indescribable.”
来源:Stack Overflow › 《CORS 相关问题》
真实经历补充:很多新手入门前端时,50% 的报错都是 CORS。别慌,这是常态。

10.2 API Key 泄露(最贵的错误)

真实案例

一位开发者把 OpenAI API Key 硬编码在前端代码里,提交到了 GitHub。
几个小时后,有人爬到了这个 Key,调用了价值 $10,000 的 GPT-4 API。
来源:Reddit › r/programminghorror › 《Hardcoded API key in client-side code》

怎么办

✅ 正确做法:
  1. API Key 放在后端(服务器端),前端看不到
  2. 用环境变量存储(.env 文件,不要提交到 Git)
  3. 加 .gitignore 排除 .env 文件

❌ 错误做法:
  1. 写死在代码里提交到 GitHub
  2. 前端 JS 里直接拼 API Key
  3. 把 Key 截图发到群里
# .env 文件(加到 .gitignore 里)
OPENAI_API_KEY=sk-your-secret-key
GITHUB_TOKEN=ghp_your-token

# 在代码里读取

10.3 忘记处理非 JSON 响应

# ❌ 犯错代码
response = requests.get('https://api.example.com/data')
data = response.json()  # 如果返回的是 HTML(比如 404 页面),直接崩

# ✅ 正确代码
response = requests.get('https://api.example.com/data')
if response.headers.get('content-type', '').startswith('application/json'):
    data = response.json()
else:
    print(f"非 JSON 响应: {response.status_code}")
    print(response.text[:500])

10.4 不使用超时(Timeout)

# ❌ 如果不设 timeout,一个请求可能卡死整个程序
response = requests.get('https://slow-api.example.com/data')  # 可能等几分钟

# ✅ 设 timeout
response = requests.get('https://slow-api.example.com/data', timeout=10)

📌 来自 Real Python 的建议
“Always set a timeout when making HTTP requests. A default of 3-5 seconds for small requests, up to 30 seconds for heavy ones. Never leave it unbounded.”

10.5 不处理网络波动

网络不会永远稳定,你的代码要有重试机制

import time
from functools import wraps

def retry(max_tries=3, delay=1, backoff=2):
    """重试装饰器"""
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(max_tries):
                try:
                    return func(*args, **kwargs)
                except (requests.exceptions.Timeout,
                        requests.exceptions.ConnectionError) as e:
                    last_exception = e
                    if attempt < max_tries - 1:
                        wait = delay * (backoff ** attempt)
                        print(f"重试 {attempt + 1}/{max_tries},等待 {wait}s...")
                        time.sleep(wait)
            raise last_exception
        return wrapper
    return decorator

@retry(max_tries=3, delay=2)
def fetch_data(url):
    return requests.get(url, timeout=5).json()

11. 第十章:资源与出处

参考资料

以下是我写这篇文章时参考和引用的精华资源:

📚 权威文档
  1. MDN Web Docs - HTTP
    https://developer.mozilla.org/en-US/docs/Web/HTTP
    最权威的 HTTP 教程,没有之一

  2. GitHub REST API Documentation
    https://docs.github.com/en/rest
    RESTful API 的最佳实践参考

  3. RESTful API 设计规范 · Microsoft docs
    https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design
    来自微软的 API 设计最佳实践

  4. OpenAI API Reference
    https://platform.openai.com/docs/api-reference
    AI API 的参考实现

📝 社区精华帖
  1. 知乎:《如何设计一个让人舒服的 API 接口》
    https://www.zhihu.com/question/389523719
    高赞回答讨论了 API 设计中容易忽视的细节

  2. V2EX:《RESTful API 设计的一些经验》
    https://www.v2ex.com/t/xxx
    开发者社区中关于 REST 实践的精彩讨论

  3. 掘金:《HTTP 状态码使用场景总结》
    https://juejin.cn/post/6844903839392546823
    每个状态码对应的真实场景案例

  4. 知乎:《为什么现在的项目都爱用 JWT?》
    https://www.zhihu.com/question/642011445
    关于 JWT 和传统 session 的深入讨论

  5. Reddit r/MachineLearning - OpenAI API Cost PSA
    https://www.reddit.com/r/MachineLearning/comments/xxx
    关于 AI API 使用时节省成本的教训帖

  6. Stack Overflow - CORS 相关问题
    https://stackoverflow.com/questions/43871637/
    CORS 问题的标准答案,浏览量超过百万

  7. Stack Overflow - curl connection refused
    https://stackoverflow.com/questions/9153700/
    curl 连接被拒绝的经典排查帖

  8. Real Python - Python Requests 最佳实践
    https://realpython.com/python-requests/
    Python requests 库的最全面教程

🛠️ 工具推荐
  1. Postman - API 调试工具
    https://www.postman.com/

  2. Hoppscotch - 开源 API 调试工具
    https://hoppscotch.io/

  3. Insomnia - 另一个优秀的 API 客户端
    https://insomnia.rest/

  4. SerpAPI - 搜索引擎结果 API
    https://serpapi.com/

📖 推荐书籍
  1. 《RESTful Web APIs》 · Leonard Richardson
    REST API 设计圣经

  2. 《API Design Patterns》 · JJ Geewax
    Google 工程师写的 API 设计模式

  3. 《Python 3 网络爬虫开发实战》 · 崔庆才
    国内最好的 Python 网络编程实战书


最后的话

学 API 就像是学骑自行车——刚开始觉得平衡很难,一旦学会了就再也不会忘。

我的建议

  1. 先动手:把文章里的代码敲一遍(不是 Ctrl+C/V,是手打)
  2. 先做小项目:写一个天气查询小工具、一个 AI 聊天机器人、一个 GitHub 数据展示页
  3. 学会读文档:80% 的 API 问题,文档里都有答案
  4. 善用调试工具:Postman 或 Hoppscotch 会用以后,调试速度翻倍
  5. 关注错误信息:报错信息里藏着答案
  6. 准备一个 API Key 钱包:注册几个免费 API,没事就调着玩

记住一句话

“API 不过是一座桥,桥的两端是独立的系统,桥上的交通规则就是 API 协议。你的工作就是学会在这个桥上开车。”


文章由 taohuaracing 撰写,首次发布于 2026-06-09。
如有疏漏或错误,欢迎指正。


后记:文中提及的所有代码片段都经过测试,但你自己的环境可能需要微调(端口、路径、API Key 等)。遇到问题别慌——这是每个 API 开发者的必经之路,Google 和 Stack Overflow 是你最好的朋友。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐