3. HTTP RESTful API集成
3.1 系统集成原理
在 IoT 系统中,设备与云端的交互通常通过 HTTP RESTful API 完成。本章涵盖四个核心环节:接口注册(设备身份绑定)、请求构造与签名(身份与完整性验证)、响应解析(标准化数据提取)以及错误重试机制(非幂等与幂等操作的容错策略)。整体集成模型如下图所示:
3.2 云端接口注册流程
设备首次上线需通过注册接口获取唯一身份凭证。典型注册请求如下:
POST /api/v1/devices/register
Content-Type: application/json
{
"device_name": "sensor_temp_01",
"device_type": "temperature",
"mac_address": "AA:BB:CC:DD:EE:FF",
"firmware_version": "1.2.0"
}
成功响应返回包含 device_id 和 api_secret 的 JSON:
HTTP/1.1 200 OK
Content-Type: application/json
{
"device_id": "d-abc12345",
"api_secret": "sk_live_xxxxx",
"token_expires_in": 86400
}
安全提示:
api_secret 仅首次返回,设备务必持久化至安全存储(如 TPM 模块或加密 Flash)。云端侧应支持密钥轮换接口。3.3 请求构造与签名
后续所有 API 请求必须携带 HMAC-SHA256 签名。签名生成步骤:
- 规范请求方法、路径、查询参数、时间戳、请求体。
- 使用
api_secret计算 HMAC-SHA256。 - 通过 HTTP 头
Authorization或自定义头传递签名。
以下为 Python 签名示例:
import hmac, hashlib, json, time, requests
def sign_request(method, path, body, secret):
timestamp = str(int(time.time()))
payload = f"{method}\n{path}\n{timestamp}\n{json.dumps(body, sort_keys=True)}"
signature = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()
return signature, timestamp
secret = "sk_live_xxxxx"
method = "POST"
path = "/api/v1/devices/data"
body = {"temperature": 25.3, "humidity": 60.2}
sig, ts = sign_request(method, path, body, secret)
headers = {
"X-Device-ID": "d-abc12345",
"X-Timestamp": ts,
"Authorization": f"HMAC {sig}",
"Content-Type": "application/json"
}
response = requests.post(f"https://iot-api.example.com{path}",
headers=headers, json=body)
3.4 响应解析
云端返回统一错误码规范,示例响应格式:
HTTP/1.1 200 OK
Content-Type: application/json
{
"code": 0,
"message": "success",
"data": {
"temperature": 25.3,
"humidity": 60.2,
"recorded_at": "2025-03-21T10:30:00Z"
}
}
错误码一览表:
| HTTP 状态码 | 业务 code | 含义 | 是否重试 |
|---|---|---|---|
| 200 | 0 | 成功 | 否 |
| 401 | 1001 | 签名无效 | 否(检查时间戳/密钥) |
| 403 | 1002 | 设备未注册 / 权限不足 | 否 |
| 429 | 2001 | 请求频率超限 | 是(指数退避) |
| 500 | 3001 | 服务器内部错误 | 是 |
| 502/503 | 3002 | 网关超时或服务不可用 | 是 |
3.5 错误重试机制
实现指数退避重试策略:
import time, requests
def request_with_retry(url, headers, body, max_retries=3, base_delay=1):
for attempt in range(max_retries + 1):
try:
resp = requests.post(url, headers=headers, json=body, timeout=5)
if resp.status_code == 200:
return resp.json()
if resp.status_code in [429, 500, 502, 503]:
if attempt == max_retries:
raise Exception(f"Max retries exceeded, last status {resp.status_code}")
delay = base_delay * (2 ** attempt)
time.sleep(delay)
continue
else:
# 4xx 非重试错误直接抛出
resp.raise_for_status()
except requests.exceptions.Timeout:
if attempt == max_retries:
raise
delay = base_delay * (2 ** attempt)
time.sleep(delay)
return None
重要:仅对幂等请求(如获取数据、上报已去重的传感器值)使用自动重试。非幂等操作(如创建设备、发送命令)应避免重试,或先查询确认。
3.6 调试方法
使用 curl 验证签名逻辑是否正确:
# 计算签名(使用 openssl 辅助)
TIMESTAMP=$(date +%s)
PAYLOAD="POST\n/api/v1/devices/data\n${TIMESTAMP}\n{\"temperature\":25.3}"
SIGNATURE=$(echo -e "$PAYLOAD" | openssl dgst -sha256 -hmac "sk_live_xxxxx" | awk '{print $NF}')
curl -X POST "https://iot-api.example.com/api/v1/devices/data" \
-H "X-Device-ID: d-abc12345" \
-H "X-Timestamp: ${TIMESTAMP}" \
-H "Authorization: HMAC ${SIGNATURE}" \
-H "Content-Type: application/json" \
-d '{"temperature":25.3}'
3.7 部署步骤
- 云端侧:部署注册服务端点、签名验证中间件、设备状态数据库(Redis/MySQL)。
- 设备侧:集成 HTTP 客户端库,将签名逻辑封装为独立模块。
- 测试环境:使用 mock 服务器(如
pytest-httpserver)模拟注册和常规 API 响应。 - 生产环境:配置 TLS 1.2+,禁止明文 HTTP 传输。
3.8 常见错误排查
| 现象 | 可能原因 | 排查步骤 |
|---|---|---|
| 注册返回 403 | MAC 地址已被注册或格式错误 | 检查 MAC 格式,联系管理后台释放 |
| 401 签名无效 | 时间戳偏差超过 5 分钟,或密钥错误 | 同步 NTP,打印签名串比对云端日志 |
| 请求一直超时 | 网络不可达或 DNS 解析失败 | 使用 ping 和 curl -v 测试 |
| 响应解析失败 | 云端返回非 JSON 或字段不匹配 | 打印原始响应体,启用严格 JSON 解析 |
| 重试陷入死循环 | 未区分幂等操作 | 在重试逻辑前添加幂等检查标志 |
3.9 性能与稳定性优化
- 连接池:使用
requests.Session()或aiohttp的连接复用,减少 TCP 握手。 - 签名缓存:同一时刻的多个请求可复用时间戳(精度秒级),减少 HMAC 计算开销。
- 异步重试:使用
tenacity库或asyncio实现非阻塞重试,避免阻塞主循环。 - 分布式限流:云端侧使用令牌桶算法,设备侧根据
Retry-After头等待。