☰
DDNS Callback Provider 实战指南:把 IP 变更推送到任意 Webhook 或 HTTP API
2026/10/13 11:09:59 网站建设 项目流程
  • 网络
  • CLI

【免费下载链接】DDNS

🌐自动更新域名解析到本机IP,支持dnspod,阿里DNS,CloudFlare,华为云,DNSCOM...

项目地址:https://gitcode.com/gh_mirrors/dd/DDNS
点击查看免费下载

本文基于 DDNS 项目的docs/en/providers/callback.md文档,系统讲解 Callback Provider 的完整配置方法、GET/POST 请求机制与变量替换规则,并结合 ddns/provider/callback.py 的源码实现,剖析其 HTTP 响应判定、失败重试与缓存写入的底层逻辑。读完你可以把 DDNS 的 IP 更新请求转发到任意自定义 HTTP API 或 Webhook,并能正确处理endpoint/id/token三个参数的组合以及变量替换的边界情况。

一、Callback Provider 是什么

Callback Provider 是 DDNS 内置的一个通用自定义回调接口(dns取值固定为"callback"),它允许把每次 DDNS 更新请求转发到任意自定义的 HTTP API 端点或 Webhook。相比其他必须适配特定云厂商 API 的 Provider,它具备三个特点:

  • 请求地址完全由用户自定义,支持 GET 和 POST 两种方法;
  • 支持内置变量替换(__DOMAIN__、__IP__等),请求时自动填充;
  • 不解析响应体内容,仅以 HTTP 状态码判定成败。

从源码结构看,CallbackProvider 继承自 SimpleProvider 抽象基类,只实现了set_record一个方法(简单型 Provider 的统一接口),并显式声明了三个类属性:

class CallbackProvider(SimpleProvider): endpoint = "" # 无固定 API endpoint,直接用 id 作为 URL content_type = TYPE_JSON # POST 请求体按 application/json 编码 decode_response = False # 响应体不做 JSON 解码,原样返回 _require_http_success = True # 只有 2xx 状态码才算成功

其中_require_http_success = True是理解后文"HTTP 响应判定"一节的关键:它让基类把所有非 2xx 响应一律视为失败,而不是像多数 Provider 那样把响应体解析为 JSON 结果。

二、基本配置参数

参数说明必填示例
id回调 URL 地址,支持变量替换✅https://api.example.com/ddns?domain=__DOMAIN__&ip=__IP__
tokenPOST 请求参数(JSON 对象或 JSON 字符串),为空时使用 GET 请求可选{"api_key": "your_key"}或"{\"api_key\": \"your_key\"}"
endpoint可选的 API 端点地址,不参与变量替换可选https://api.example.com/ddns
dns固定值"callback",表示使用回调方式✅"callback"

四个参数在配置加载时由 ddns/config/config.py 统一提取(id、token、endpoint均在公共字段列表中),再经 ddns/main.py 中的run()传入 Provider 构造函数。

三、完整配置示例

{ "$schema": "https://ddns.newfuture.cc/schema/v4.1.json", "dns": "callback", "endpoint": "https://api.example.com", // endpoint 可以和 id 参数合并 "id": "/ddns?domain=__DOMAIN__&ip=__IP__", // endpoint 和 id 不能同时为空 "token": "", // 空字符串表示使用 GET 请求,有值时使用 POST 请求 "index4": ["url:http://api.ipify.org", "public"], "index6": "public", "ipv4": "ddns.newfuture.cc", "ipv6": ["ddns.newfuture.cc", "ipv6.ddns.newfuture.cc"] }

公用参数说明

除上述四个专属参数外,Callback Provider 支持以下公用配置项(完整取值说明参见 配置文件格式):

参数说明类型取值范围/选项默认值参数类型
index4IPv4 来源数组参考配置default公用配置
index6IPv6 来源数组参考配置default公用配置
ipv4IPv4 域名数组域名列表无公用配置
ipv6IPv6 域名数组域名列表无公用配置
proxy代理设置数组参考配置无公用网络
sslSSL 验证布尔/字符串"auto"、true、falseauto公用网络
cache缓存设置布尔/字符串true、false、filepathtrue公用配置
log日志配置对象参考配置无公用配置

注意ttl和line两个公用参数与 Callback Provider 直接相关:ddns/main.py 在调用set_record时会把config.ttl和config.line作为关键字参数传入,它们随后会被填充到__TTL__和__LINE__变量中(见第六节)。

四、请求方式:GET 与 POST

请求方法由token是否为空自动决定:

方法条件描述
GETtoken为空使用 URL 查询参数
POSTtoken非空使用 JSON 请求体

GET 请求示例

{ "$schema": "https://ddns.newfuture.cc/schema/v4.1.json", "dns": "callback", "id": "https://api.example.com/update?domain=__DOMAIN__&ip=__IP__&type=__RECORDTYPE__", "index4": ["url:http://api.ipify.org", "public"], "ipv4": "ddns.newfuture.cc" }

变量替换后实际发出的请求为:

GET https://api.example.com/update?domain=ddns.newfuture.cc&ip=192.168.1.100&type=A

POST 请求示例

{ "$schema": "https://ddns.newfuture.cc/schema/v4.1.json", "dns": "callback", "endpoint": "https://api.example.com", "token": { "api_key": "your_secret_key", "domain": "__DOMAIN__", "value": "__IP__" }, "index4": ["url:http://api.ipify.org", "public"], "ipv4": "ddns.newfuture.cc" }
POST https://api.example.com Content-Type: application/json { "api_key": "your_secret_key", "domain": "ddns.newfuture.cc", "value": "192.168.1.100" }

POST 场景下token支持两种写法:直接写 JSON 对象,或写一个 JSON 字符串(源码中通过jsondecode(token)反序列化后再逐值替换变量)。如果token字符串不是合法 JSON,反序列化会抛出ValueError,这一点在 tests/test_provider_callback.py 的test_json_decode_error_handling中有明确验证。

五、HTTP 响应判定与缓存机制

Callback 对响应的判定规则非常明确:

  • 仅将最终 HTTP 状态为 200–299的响应视为成功,允许响应体为空(例如 HTTP 204);
  • HTTP 404、429 及所有其他非 2xx 状态均视为失败,本次 IP不会写入成功缓存,下一次同步时仍会重试;
  • Callback不解析HTTP 200 响应体中的自定义业务错误。如果要报告更新失败,回调接口应当返回适当的非 2xx HTTP 状态码。

这些规则在源码中有对应的实现链条:

  1. 基类 SimpleProvider._http 中,400/401/403/5xx 会直接抛出RuntimeError;对于其他非 2xx 状态,由于CallbackProvider._require_http_success = True,会在 第 269–270 行 抛出自定义HTTP error [状态码]异常;
  2. CallbackProvider.set_record 用try/except捕获所有异常并返回False,2xx 空响应体(res非None的空串)则返回True;
  3. 主流程 update_ip 只在result为真时才执行cache[cache_key] = address写入缓存——这就是"失败不缓存、后续重试"的机制来源。

tests/test_provider_callback.py 中的TestCallbackProviderHTTPResponses测试类用隔离的磁盘缓存完整验证了这条链条:对 404/400/429 响应断言update_ip返回False且缓存文件内容为空{};对 200/201/204/299 空响应断言成功并写入{domain + ":A": address};随后再次同步时因缓存命中,不再发出任何 HTTP 请求(见 _assert_failure_retries)。此外 test_http_200_business_error_body_is_not_interpreted 验证了{"success": false, ...}这类 200 业务错误体不会被解读,仍判定为成功。

一个实用细节:基类的 _mask_sensitive_data 会在输出请求 URL 和响应日志前对token做打码(保留首尾 2 字符 +***),因此即使把密钥放在token中,日志里也不会出现明文。

六、变量替换机制详解

Callback Provider 支持以下内置变量,在请求时自动替换:

变量说明示例值
__DOMAIN__完整域名sub.example.com
__IP__IP 地址(IPv4 或 IPv6)192.168.1.100或2001:db8::1
__RECORDTYPE__DNS 记录类型A、AAAA、CNAME
__TTL__生存时间(秒)300、600
__LINE__解析线路default、unicom
__TIMESTAMP__当前时间戳1634567890.123

源码层面,set_record 在每次更新时构造变量映射并调用 _replace_vars 完成替换:

extra.update({ "__DOMAIN__": domain, "__RECORDTYPE__": record_type, "__TTL__": ttl, "__IP__": value, "__TIMESTAMP__": time(), "__LINE__": line, }) url = self._replace_vars(url, extra)

其中domain、value、record_type来自主流程的每次同步,ttl和line则透传自配置文件中的ttl、line参数;__TIMESTAMP__取time.time()的浮点时间戳。替换逻辑是对映射中每个键做str.replace,且 URL 与 POST 请求体都经过替换——但仅字符串类型的值会被替换:测试 test_set_record_post_method_mixed_types 验证了token中的数字(12345)、整数(30)、布尔(True)均原样发送,只有字符串值中的变量被替换。若 URL 或请求体中出现了映射之外的未知变量(如__UNKNOWN__),会保持原样不做替换(见 test_replace_vars_partial_matches)。

仓库自带的 E2E 配置 tests/config/callback.json 展示了全部 6 个变量的真实用法:id中用__DOMAIN__/__IP__/__RECORDTYPE__/__TIMESTAMP__构造查询串,token的 JSON 字符串中同时携带__TTL__、__LINE__并附加自定义字段(event、version等),是一个可直接参考的完整样例。

七、源码实现核对与关键细节

对照 ddns/provider/callback.py 的实现,有几点值得在使用时特别注意:

  1. id必须是完整 URL。_validate 在初始化时执行校验:只要id为空或不包含://,就会抛出ValueError("endpoint or id must be configured with URL")。也就是说,文档中"endpoint与相对路径id合并"的写法在当前源码的校验逻辑下会被拒绝——从源码结构看,当前版本要求把完整回调 URL(含协议头)直接写在id中最为稳妥。这一点与 test_validate_failure_empty_id 的行为一致。
  2. token为空与 None 等价。判定逻辑是if token:真值判断(callback.py 第 44–52 行),空字符串走 GET 分支,非空则解析为 POST 请求体;token为 dict 时直接使用,为字符串时先jsondecode反序列化。
  3. 响应体原样返回。由于decode_response = False,基类 _http 不会把响应体当作 JSON 解析,set_record拿到的是原始响应文本并记录到日志(Callback result: %s),便于在运行日志中直接看到回调服务的原始应答。
  4. 失败语义统一。无论是网络异常、非 2xx 状态还是请求构造错误,set_record一律记录错误日志并返回False,由主流程决定本次run()的退出码(ddns/main.py 中任一域名更新失败会以退出码 1 结束),从而保证任务调度器能正确感知失败并在下一周期重试。

八、典型使用场景

场景 1:自定义 Webhook 通知

将 DDNS 更新事件推送到自定义 Webhook,token中通过变量拼装完整事件负载:

{ "endpoint": "https://hooks.example.com", "id": "/webhook", "token": { "event": "ddns_update", "domain": "__DOMAIN__", "new_ip": "__IP__", "record_type": "__RECORDTYPE__", "timestamp": "__TIMESTAMP__" }, "dns": "callback", "index4": ["default"] }

场景 2:使用字符串格式的 token

当需要动态构造复杂 JSON 字符串(例如嵌套结构)时,token可以写成 JSON 字符串形式,效果与对象形式等价:

{ "id": "https://api.example.com/ddns", "token": "{\"auth\": \"your_key\", \"record\": {\"name\": \"__DOMAIN__\", \"value\": \"__IP__\", \"type\": \"__RECORDTYPE__\"}}", "dns": "callback" }

九、故障排除

调试方法

  1. 启用调试:在配置中设置"debug": true(日志级别相关配置见 配置文件格式);
  2. 查看日志:检查 DDNS 运行日志,Callback Provider 会记录每次请求的GET/POST URL、body、response以及最终结果(Callback result: ...);
  3. 测试 API:用 curl 或 Postman 独立测试回调 API 本身,排除服务侧问题;
  4. 网络检查:确保网络连通性和 DNS 解析正常,代理配置(proxy参数)生效。

使用 curl 测试回调接口

# 测试 GET 请求 curl "https://httpbin.org/get?domain=test.example.com&ip=192.168.1.1" # 测试 POST 请求 curl -X POST "https://httpbin.org/post" \ -H "Content-Type: application/json" \ -d '{"domain": "test.example.com", "ip": "192.168.1.1"}'

排查时若发现回调服务收到的是 2xx 但业务处理失败,请记住第五节的规则:回调接口必须用非 2xx 状态码反馈失败,否则 DDNS 会将本次 IP 写入缓存、不再重试。

十、延伸阅读

  • 配置文件格式:index4、ipv4、proxy、log等公用参数的完整说明
  • 命令行使用:以 CLI 方式运行 DDNS 与调试
  • 开发者指南:编写自定义 Provider 的规范(Callback Provider 即基于SimpleProvider接口)
  • 核心实现:ddns/provider/callback.py、ddns/provider/_base.py
  • 主流程与缓存写入:ddns/main.py
  • 单元测试与响应判定验证:tests/test_provider_callback.py
  • E2E 回调配置样例:tests/config/callback.json
  • 网络
  • CLI

【免费下载链接】DDNS

🌐自动更新域名解析到本机IP,支持dnspod,阿里DNS,CloudFlare,华为云,DNSCOM...

项目地址:https://gitcode.com/gh_mirrors/dd/DDNS
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询