封装 API 客户端
约 1287 字大约 4 分钟
2026-05-10
阅读导览
封装 API 客户端
在项目中,不建议到处散落 requests.get 和 requests.post。更好的方式是把第三方接口封装成 API Client。
- 为什么要封装
- 一个简单客户端
- 统一请求方法
- 错误处理
实践清单
学习练习清单
- 1不要让 requests.get(...) 散落全代码:写一个 Client 类,集中 base_url、headers、timeout、Token。
- 2封装统一的 _request() 中央方法,做日志/异常/重试;业务方法只暴露 .get_user(id) 这种语义化接口。
- 3Token 永远从 os.environ 或配置文件读,绝不硬编码 —— Git 历史里搜出 Token 是经典安全事故。
- 4重复 3 次以上才封装;一次性脚本直接 requests.get 就好,过早抽象比重复更糟。
- 5复杂项目里用 dataclass/Pydantic 给响应建模,IDE 自动补全 + 静态检查比裸 dict 安全得多。
在项目中,不建议到处散落 requests.get() 和 requests.post()。更好的方式是把第三方接口封装成 API Client。
为什么要封装
封装 API Client 的好处:
- 统一 base URL
- 统一 Header 和 Token
- 统一超时
- 统一错误处理
- 统一重试策略
- 调用方不关心 HTTP 细节
- 方便测试和替换实现
一个简单客户端
import requests
class GitHubClient:
def __init__(self, token, base_url='https://api.github.com'):
self.base_url = base_url
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {token}',
'Accept': 'application/vnd.github+json',
'User-Agent': 'my-github-client/1.0',
})
def get_user(self, username):
response = self.session.get(
f'{self.base_url}/users/{username}',
timeout=5,
)
response.raise_for_status()
return response.json()调用方:
client = GitHubClient(token='xxx')
user = client.get_user('python')统一请求方法
可以把公共逻辑集中到 _request()。
class ApiClient:
def __init__(self, base_url, token):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
self.session.headers.update({'Authorization': f'Bearer {token}'})
def _request(self, method, path, **kwargs):
url = f'{self.base_url}/{path.lstrip('/')}'
kwargs.setdefault('timeout', 5)
response = self.session.request(method, url, **kwargs)
response.raise_for_status()
return response.json()
def get(self, path, **kwargs):
return self._request('GET', path, **kwargs)
def post(self, path, **kwargs):
return self._request('POST', path, **kwargs)错误处理
不要把所有异常都吞掉。可以定义业务异常:
class ApiError(Exception):
pass然后包装底层异常:
try:
response = self.session.request(method, url, **kwargs)
response.raise_for_status()
except requests.RequestException as exc:
raise ApiError(f'API 请求失败:{exc}') from excToken 管理
Token 不应该硬编码在代码中。可以从环境变量读取:
import os
token = os.environ['GITHUB_TOKEN']类型提示
def get_user(self, username: str) -> dict:
...更复杂项目中,可以用 dataclass 或 Pydantic 定义响应结构。
设计建议
- 一个客户端类对应一个外部服务。
- 方法名使用业务语义,例如
send_sms(),而不是暴露所有 HTTP 细节。 - 默认设置超时。
- 集中处理错误。
- 日志要脱敏,不要打印 Token。
从复制粘贴到封装
刚开始调用 API 时,你可能会写出很多重复代码:
import requests
headers = {'Authorization': 'Bearer token'}
response = requests.get('https://api.example.com/users', headers=headers, timeout=5)
response.raise_for_status()
users = response.json()
response = requests.get('https://api.example.com/orders', headers=headers, timeout=5)
response.raise_for_status()
orders = response.json()这段代码能跑,但有几个问题:
- base URL 重复;
- headers 重复;
- timeout 重复;
- 错误处理重复;
- 将来 Token 变化时要改很多地方。
封装 API Client 的目的不是“显得高级”,而是把这些重复规则集中到一个地方。
一个更完整但仍然简单的 Client
import requests
class ApiClient:
def __init__(self, base_url, token=None, timeout=5):
self.base_url = base_url.rstrip('/')
self.timeout = timeout
self.session = requests.Session()
if token:
self.session.headers.update({
'Authorization': f'Bearer {token}',
})
def request(self, method, path, **kwargs):
url = self.base_url + path
kwargs.setdefault('timeout', self.timeout)
response = self.session.request(method, url, **kwargs)
response.raise_for_status()
return response
def get_json(self, path, params=None):
response = self.request('GET', path, params=params)
return response.json()
def post_json(self, path, data):
response = self.request('POST', path, json=data)
return response.json()
client = ApiClient('https://api.example.com', token='your-token')
users = client.get_json('/users', params={'page': 1})这个版本已经集中处理了 base URL、Session、Token、timeout 和状态码检查。
什么时候不要过度封装
如果脚本只有一个请求,直接写 requests.get() 没问题。过早封装反而会增加理解成本。
适合封装的信号:
- 同一个 base URL 请求超过 3 个;
- 每个请求都要带相同 Header;
- 每个请求都要统一处理错误;
- 以后可能切换 Token、代理、超时或重试策略;
- 多个文件都会调用同一组 API。
不适合封装得太复杂的情况:
- 只是一次性数据脚本;
- 接口数量很少;
- 团队里其他人看不懂你封装出来的抽象。
给方法起清楚的名字
API Client 里可以继续封装业务方法:
class ApiClient:
# 省略前面的 __init__ 和 request
def list_users(self, page=1):
return self.get_json('/users', params={'page': page})
def get_user(self, user_id):
return self.get_json(f'/users/{user_id}')
def create_user(self, name, email):
return self.post_json('/users', {
'name': name,
'email': email,
})调用时就更像在表达业务意图:
client = ApiClient('https://api.example.com')
user = client.get_user(1)初学阶段不必追求“完美架构”,先做到少重复、名字清楚、错误能看懂。
总结
API Client 是把“能发请求”的代码变成“可维护接口调用”的关键。它让网络请求更统一、更安全,也更容易测试。
重点总结
要点回收
- 封装 API Client 是为了集中 base URL、Header、timeout、错误处理等重复规则。
- 请求不多时不用强行封装;重复出现三五次后再抽象通常更自然。
- 好的 Client 方法名应该表达业务意图,而不是让调用处到处拼 URL。
版权所有
版权归属:Shuo Liu
