错误处理
公开 API 使用标准 HTTP 状态码与 JSON 响应。客户端应区分“请求失败”“成功但暂时没有数据”和“节点已经离线”。
状态码
| 状态 | 含义 | 客户端处理 |
|---|---|---|
200 | 请求成功 | 解析 JSON,同时接受 null、空数组与新增字段 |
204 | CORS 预检成功 | 不读取响应体 |
400 | 缺少参数或参数无效 | 修正请求,不要原样重试 |
401 | Token 或 Session 无效 | 仅适用于受保护接口;不要记录凭据 |
403 | Origin、权限或安全策略拒绝 | 检查精确 Origin 与权限范围 |
404 | 资源或目标不存在 | 检查 ID、启用状态与部署版本 |
413 | 请求体或上传包过大 | 缩小请求或扩展 ZIP |
415 | Content-Type 不支持 | 扩展上传使用允许的 ZIP Content-Type |
429 | 触发速率限制 | 读取 Retry-After(若存在)并指数退避 |
500–504 | 服务端或上游暂时异常 | 有限次数重试并加入随机抖动 |
推荐重试策略
只重试幂等的 GET 请求,并限制尝试次数:
js
async function getJson(url, attempts = 3) {
for (let attempt = 0; attempt < attempts; attempt += 1) {
const response = await fetch(url, { credentials: 'omit' })
if (response.ok) return response.json()
if (![429, 500, 502, 503, 504].includes(response.status)) {
throw new Error(`Request rejected: HTTP ${response.status}`)
}
const retryAfter = Number(response.headers.get('retry-after') || 0) * 1000
const backoff = Math.max(retryAfter, 500 * (2 ** attempt))
const jitter = Math.floor(Math.random() * 250)
await new Promise(resolve => setTimeout(resolve, backoff + jitter))
}
throw new Error('API temporarily unavailable')
}空数据不是错误
以下响应都可能是正常状态:
- 新 Agent 尚未完成第一次上报,
latest为null; - 请求窗口内没有 Ping,
pings为空; - 外部 Latency 节点已经过 stale 窗口,
sources为空; - 目标启用了隐藏公网地址,Cloudflare 与外部 Latency 不对外展示;
- 指标字段在虚拟化环境不可用,例如温度为
null。
UI 应显示“暂无数据”或“尚未上报”,不要把这些情况渲染成 JavaScript 异常。
兼容性要求
v1 可以增加可选字段,但不会静默删除或重命名既有字段。客户端应:
- 检查
api_version; - 忽略未知对象字段;
- 不依赖 JSON 属性顺序;
- 对时间戳显式排序;
- 对
null、缺失字段和空数组提供默认行为; - 不把错误消息直接插入
innerHTML。
浏览器 CORS 报错
命令行请求成功但浏览器失败,通常是 Origin 没有加入 DEVELOPER_API_ORIGINS。白名单必须使用完整 Origin,例如 https://status.example.com,不能包含路径,也不能使用 *。HTTP 仅允许 localhost、127.0.0.1 和 [::1] 开发地址。