雨云 DNS 接入返工记:端点、返回结构、尾斜杠,文档里一个都没写

起因:一句「找不到雨云域名」

狸花云的 DDNS 托管支持四家服务商,雨云是最后接入的一家。配置界面上填好 API Key、填好域名,点同步,红字弹回来:

找不到雨云域名:xuanwork.top(请到雨云控制台确认域名已添加,且 API Key 有 domain 权限)

第一反应当然是怀疑权限。去雨云控制台确认:域名在、Key 也在、权限没问题。于是打开 Chrome DevTools,直接手打接口。

三次返工就从这里开始。

坑一:端点路径不是文档里写的那样

早期按网上流传的写法,记录接口是 /dns/domain/。实测这个路径一律返回 404,连鉴权都过不去。

雨云的 API 文档(api.v2.rainyun.com/openapi.json)给出的真实路径是 /product/domain/——域名和记录都挂在产品体系下,不是独立的 DNS 模块。

操作 错误写法 正确写法
域名列表 /dns/domain/ /product/domain/
记录列表 /dns/domain/{id}/record/ /product/domain/{id}/dns
鉴权头 Authorization: Bearer x-api-key:

顺带一个细节:域名列表的 options 参数是必填的。不带参数直接 GET,返回的是:


{"code":10002,"message":"输入参数无效"}

带上 options={}(空对象)才正常返回。这种”空参数也是参数”的设计,不试一次根本不会知道。

坑二:返回结构里套了一层

端点改对之后,域名列表能拿到了。但代码里的解析是这么写的:


const list = r.data?.data || r.data?.list || r.data?.rows

按常见约定猜的,数组直接在 data 下。而雨云实际返回的是:


{
  "code": 200,
  "data": {
    "TotalRecords": 1,
    "Records": [
      { "id": 1222, "domain": "xuanwork.top", "status": "ok" }
    ]
  }
}

data 是个对象,不是数组。data.find() 直接抛错,被 try 接住,然后一路走到”找不到域名”——错误信息还是我上一版刚改的那句,看起来像权限问题,实际是解析问题。

修正就一行:


const raw = r.data?.data
const list = Array.isArray(raw) ? raw : (raw?.Records || raw?.records || raw?.list || [])

加上 Records 这一路兜底,TotalRecords / Records 这种”总数字段在前、数据在后”的分页风格就跑通了。

坑三:尾斜杠会 301

记录接口带不带尾斜杠,结果完全不同:


# 301 Moved Permanently,body 是一段 HTML
curl -H "x-api-key: $K" \
  "https://api.v2.rainyun.com/product/domain/1222/dns/?limit=5&page_no=1"

# 200,正常 JSON
curl -H "x-api-key: $K" \
  "https://api.v2.rainyun.com/product/domain/1222/dns?limit=5&page_no=1"

301 的响应体是 Moved Permanently,JSON.parse 一挂,又被 catch 吞掉。这类”解析失败被静默”的写法很危险——建议所有 provider 的 error 都至少 console.warn 一次,不然出问题全靠肉眼猜。

验证:幂等比”成功”更有信息量

三处改完,跑一次真实调用:


zoneId = 1222
upsert = {"ok":true,"unchanged":true,"id":151040519}

unchanged: true 比 ok: true 更让人放心——说明它不只知道”我能写”,还知道”这条记录已经是这个值,不用动”。DDNS 每 2 分钟跑一次,幂等做不对,就是每 2 分钟刷一次接口。

附带:容器信息不再靠手动补报

这版还顺手改了一件运维上的事:容器清单改成后端每 10 分钟自动拉取,启动 3 分钟后先跑一次。以前新开的容器要等用户点「一键补报」,节点上多了几台机器,面板里就是一堆空白。

顺带修掉一个转义 bug:生成上报脚本时,tools 数组被拼成了 [curl,wget](缺引号),服务端按 JSON 解析直接 400。

项目 改前 改后
容器信息来源 手动一键补报 后端每 10 分钟自动拉
新容器 等用户点一次 初始化脚本装 systemd timer / cron
上报 JSON tools: [curl,wget] 400 正确转义

小结

这三个坑有个共同点:都不在文档里,而且失败时看起来都像权限问题。

对接第三方 API,能省时间的三件事:直接读 openapi.json 而不是博客教程;先手打一次 curl 看原始响应,再写解析;任何被 catch 吞掉的错误,至少留一行日志。

💬 留下你的爪印