4. RESTful API 接口对接:HTTP/HTTPS 协议、RESTful 规范、请求/响应封装、常见 API 认证方式
数据源对接中,八成以上都是 RESTful API。说白了,就是通过 HTTP 协议跟远程服务“对话”。我最早接触时以为只是发个 GET 请求那么简单,结果被各种状态码和认证搞得焦头烂额。这一节咱们把关键点捋清楚。
4.1 HTTP/HTTPS 协议要点
HTTP 是应用层协议,基于 TCP。HTTPS 就是在外面套了一层 TLS/SSL。我个人习惯这么记:HTTP 是明信片,谁都能看;HTTPS 是信封,只有收件人能拆。
- 请求方法:GET(查)、POST(增)、PUT(全量改)、PATCH(部分改)、DELETE(删)。
- 状态码:2xx 成功,3xx 重定向,4xx 客户端错误,5xx 服务端错误。常见的 200、201、204、400、401、403、404、500。
- Header:Content-Type、Authorization、Accept、User-Agent 等。特别留意 Content-Type: application/json。
我的经验:调试时先用 curl 模拟一遍,避免代码里藏着“隐形”的 Header 问题。比如服务端要求带 X-Request-ID,你忘了传,可能一直 400。
4.2 RESTful 设计规范
RESTful 不是协议,而是一套设计风格。核心是用资源路径 + 动词来表达操作。
- 资源用名词复数:
/users、/orders/{id}。不要用动词,比如/getUser。 - 无状态:每次请求携带全部必要信息,服务端不保存会话。
- 版本化:路径里加
/v1或 Header 里指定。 - 查询参数:
?page=1&size=20用于分页、排序、过滤。
我曾经踩过的坑:对接一个内部系统,他们把删除设计成 POST
/user/delete?id=xxx。这完全是 RPC 风格,破坏了可读性。后来我强制要求对方按 RESTful 改造,否则维护成本太高。
4.3 请求与响应封装实践
封装是让代码干净的关键。我习惯写一个 ApiClient 类,统一处理超时、重试、日志。
import requests
from requests.adapters import HTTPAdapter
class ApiClient:
def __init__(self, base_url, timeout=10):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
self.session.mount('https://', HTTPAdapter(max_retries=3))
self.timeout = timeout
def request(self, method, path, **kwargs):
url = f"{self.base_url}/{path.lstrip('/')}"
kwargs.setdefault('timeout', self.timeout)
resp = self.session.request(method, url, **kwargs)
resp.raise_for_status() # 非 2xx 抛异常
return resp.json()
注意几个细节:
- 超时一定要设,不然网络挂了你代码也跟着挂。
- 重试策略用指数退避,别一失败就猛刷。
- 响应解析前先检查 Content-Type,防止拿到的不是 JSON。
避坑指南:有些服务端返回的 HTTP 状态码是 200,但 body 里带
{"code": 1, "msg": "error"}。这种“伪成功”只能靠业务判断,别全信状态码。
4.4 常见 API 认证方式
| 方式 | 原理 | 适用场景 | 安全性 |
|---|---|---|---|
| Basic Auth | Base64(用户名:密码) 放 Header | 内部测试、简单系统 | 低(必须配合 HTTPS) |
| 静态字符串,Header 或参数传递 | 开放平台、调用量控制 | 中(易泄露,需定期轮换) | |
| OAuth2 | 授权码/密码/客户端模式获取 | 第三方授权、移动端 | 高(支持刷新、作用域) |
| JWT | 自包含 ,签名校验 | 无状态服务、微服务间 | 高(但 有效期内无法撤销) |
我个人最常用的是 OAuth2 客户端凭证模式。对于内部系统,直接配一个长期有效的 也能跑,但要记得加 IP 白名单。
# 使用 示例
headers = {'Authorization': 'Bearer YOUR_'}
response = client.request('GET', '/v1/datasources', headers=headers)
曾经的血泪教训:有一次对接厂家的 OAuth2,他们把 access_ 有效期设成 1 小时,但刷新接口有并发限制。我同时起了 10 个任务,全部过期后一齐刷新,结果被限流封了半小时。后来我强制加了 刷新锁和提前刷新机制。
整个流程串起来就是:客户端构造请求 -> 通过 HTTPS 发送 -> 服务端认证 -> 执行业务 -> 返回响应 -> 客户端解析。任何一个环节都可能出幺蛾子。我的做法是先在接口文档里确认认证方式,然后写一个最小 demo 跑通再做封装。