彩虹 DNS 面板 (dnsmgr) API 删除/修改记录返回 「域名不存在」 的根因与修复

·24 次浏览
  • 作者
    帖子
  • modiqi@modiqi
    楼主

    现象

    使用彩虹 DNS 面板 (dnsmgr v2.16,基于 ThinkPHP 6) 的 API 删除或修改 DNS 记录时,始终返回 「域名不存在」 错误,但同一凭据调用查询接口完全正常。

    API 路径格式:POST /api/record/delete/{domain_id},POST body 带 id={record_id}。

    这个问题困扰了好几个会话,期间尝试过:

    • 检查签名算法 → 正确 (md5(uid + timestamp + apikey))
    • 检查 domain_id 是否正确 → 正确 (和 list 接口用的同一个)
    • 检查权限 → 查询正常,排除权限问题

    百思不得其解。

    原因分析

    最终拿到 dnsmgr 源码才定位到根因。关键在 ThinkPHP 的参数解析机制。

    控制器代码 (Domain.php)

    // record_delete 方法
    public function record_delete($id) {
        // $id 来自路由 /api/record/delete/:id,是域名的内部 ID
        $domain = DomainModel::where('id', $id)->find();
        // ...
        $recordid = input('post.recordid');  // 注意:这里取的是 recordid,不是 id
        // ...
    }
    

    路由定义 (route/app.php)

    Route::post('/api/record/delete/:id', 'Domain/record_delete');
    

    问题所在

    ThinkPHP 的 input('param.id') 解析顺序是:路由参数 → GET → POST 。

    当我们用 POST /api/record/delete/364 且 body 带 id=2028144566343693312 时:

    1. 路由参数 :id = 364(域名 ID)✓
    2. 但 POST body 也有 id 字段,ThinkPHP 的 param 解析会让 POST 的 id 覆盖路由的 :id
    3. 控制器拿到的 $id 变成了 2028144566343693312(记录 ID)
    4. 拿记录 ID 去域名表查 → 当然查不到 →「域名不存在」

    而控制器里取记录 ID 用的是 input('post.recordid')——参数名是 recordid,不是 id 。

    同样的问题存在于 record_update

    public function record_update($id) {
        $domain = DomainModel::where('id', $id)->find();
        $recordid = input('post.recordid');  // 同样是 recordid
        // ...
    }
    

    解决方案

    将所有脚本中 delete 和 update 调用的参数从 &id= 改为 &recordid=:

    # 修复前 (错误)
    _dns_api "/api/record/delete/${domain_id}" "&id=${record_id}"
    
    # 修复后 (正确)
    _dns_api "/api/record/delete/${domain_id}" "&recordid=${record_id}"
    
    # update 同理
    _dns_api "/api/record/update/${domain_id}" "&recordid=${record_id}&name=..."
    

    验证结果

    修复后完整测试:添加测试记录 → 查询确认 → 删除 → 再次查询确认已清除,全流程通过。

    经验总结

    1. API 文档不全时读源码是最可靠的排查手段。这个 bug 纯靠黑盒调试很难定位,因为错误信息 「域名不存在」 有误导性——它暗示 domain_id 有问题,但实际是参数名写错导致 domain_id 被覆盖
    2. ThinkPHP 的 param 解析顺序(路由 → GET → POST) 是个常见坑。 POST body 中的字段名如果和路由参数名撞了,会产生意想不到的覆盖
    3. 当多个接口用同一个参数名 (id) 表示不同含义时,容易出歧义。 dnsmgr 的设计是路由用 :id 表示域名,POST 用 recordid 表示记录,避开了命名冲突——但如果调用方不知道这个约定,自然会踩坑

Cyberforums · 原文 · Markdown

想回复或继续讨论?请到 Cyberforums 赛博论坛参与。

参与讨论 / 回复