- 网络
- CLI
【免费下载链接】DDNS
🌐自动更新域名解析到本机IP,支持dnspod,阿里DNS,CloudFlare,华为云,DNSCOM...
本文基于 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__ |
token | POST 请求参数(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 支持以下公用配置项(完整取值说明参见 配置文件格式):
| 参数 | 说明 | 类型 | 取值范围/选项 | 默认值 | 参数类型 |
|---|---|---|---|---|---|
index4 | IPv4 来源 | 数组 | 参考配置 | default | 公用配置 |
index6 | IPv6 来源 | 数组 | 参考配置 | default | 公用配置 |
ipv4 | IPv4 域名 | 数组 | 域名列表 | 无 | 公用配置 |
ipv6 | IPv6 域名 | 数组 | 域名列表 | 无 | 公用配置 |
proxy | 代理设置 | 数组 | 参考配置 | 无 | 公用网络 |
ssl | SSL 验证 | 布尔/字符串 | "auto"、true、false | auto | 公用网络 |
cache | 缓存设置 | 布尔/字符串 | true、false、filepath | true | 公用配置 |
log | 日志配置 | 对象 | 参考配置 | 无 | 公用配置 |
注意ttl和line两个公用参数与 Callback Provider 直接相关:ddns/main.py 在调用set_record时会把config.ttl和config.line作为关键字参数传入,它们随后会被填充到__TTL__和__LINE__变量中(见第六节)。
四、请求方式:GET 与 POST
请求方法由token是否为空自动决定:
| 方法 | 条件 | 描述 |
|---|---|---|
| GET | token为空 | 使用 URL 查询参数 |
| POST | token非空 | 使用 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=APOST 请求示例
{ "$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 状态码。
这些规则在源码中有对应的实现链条:
- 基类 SimpleProvider._http 中,400/401/403/5xx 会直接抛出
RuntimeError;对于其他非 2xx 状态,由于CallbackProvider._require_http_success = True,会在 第 269–270 行 抛出自定义HTTP error [状态码]异常; - CallbackProvider.set_record 用
try/except捕获所有异常并返回False,2xx 空响应体(res非None的空串)则返回True; - 主流程 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 的实现,有几点值得在使用时特别注意:
id必须是完整 URL。_validate 在初始化时执行校验:只要id为空或不包含://,就会抛出ValueError("endpoint or id must be configured with URL")。也就是说,文档中"endpoint与相对路径id合并"的写法在当前源码的校验逻辑下会被拒绝——从源码结构看,当前版本要求把完整回调 URL(含协议头)直接写在id中最为稳妥。这一点与 test_validate_failure_empty_id 的行为一致。token为空与 None 等价。判定逻辑是if token:真值判断(callback.py 第 44–52 行),空字符串走 GET 分支,非空则解析为 POST 请求体;token为 dict 时直接使用,为字符串时先jsondecode反序列化。- 响应体原样返回。由于
decode_response = False,基类 _http 不会把响应体当作 JSON 解析,set_record拿到的是原始响应文本并记录到日志(Callback result: %s),便于在运行日志中直接看到回调服务的原始应答。 - 失败语义统一。无论是网络异常、非 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" }九、故障排除
调试方法
- 启用调试:在配置中设置
"debug": true(日志级别相关配置见 配置文件格式); - 查看日志:检查 DDNS 运行日志,Callback Provider 会记录每次请求的
GET/POST URL、body、response以及最终结果(Callback result: ...); - 测试 API:用 curl 或 Postman 独立测试回调 API 本身,排除服务侧问题;
- 网络检查:确保网络连通性和 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...
相关推荐
GoReleaser HTTP Upload 实战指南:把制品推送到任意 HTTP 服务器
GoReleaser HTTP Upload 实战指南:把制品推送到任意 HTTP 服务器 本文基于 GoReleaser 的 uploads 配置管道,系统讲
开发工具CI/CD构建工具GoReleaser WebHook 发布通知:把每个 Release 推送到任意 HTTP 端点
GoReleaser WebHook 发布通知:把每个 Release 推送到任意 HTTP 端点 GoReleaser 的 announce.webhook
开发工具CI/CD构建工具ddns-go 部署与配置完全指南:系统服务、Docker、Webhook 与 Callback 实战
ddns go 部署与配置完全指南:系统服务、Docker、Webhook 与 Callback 实战 ddns go 是一款开箱即用的动态 DNS(DDNS)
网络
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考