Skip to content
RuaRuan
返回

详解 CardDAV

CardDAV 是 RFC 6352​ 定义的开放标准协议,全称 vCard Extensions to Web Distributed Authoring and Versioning,本质是 WebDAV 的通讯录扩展,用来在网络上以标准化方式存储、查询、增删改和双向同步联系人。

它解决的核心问题是:手机、电脑、邮件客户端、私有云之间的通讯录不再是各管各的副本,而是共用同一个服务端”真相源”。


一、它在协议栈里的位置

HTTP/HTTPS
  └─ WebDAV (RFC 4918:PROPFIND/PUT/DELETE/MKCOL/REPORT…)
       └─ CardDAV (RFC 6352:通讯录集合、vCard 资源、查询报表)
            └─ vCard (RFC 6350:联系人数据格式,如 FN/TEL/EMAIL/PHOTO)

二、数据模型(最关键)

CardDAV 把通讯录建模成”目录树”:

例:

https://carddav.example.com/carddav/user/
                       └─ contacts/           ← Address Book Collection
                            ├─ alice.vcf      ← vCard (UID=alice)
                            └─ bob.vcf        ← vCard (UID=bob)

三、核心 HTTP 方法(客户端怎么操作)

动作用的请求
发现当前用户入口PROPFIND 查 current-user-principal
列出有哪些通讯录PROPFIND 查 addressbook-home-set
建新通讯录MKCOL + CardDAV 资源类型
拉某个联系人GET
新增/覆盖联系人PUT(带 If-Match 做并发控制)
删联系人DELETE
按条件查(如名字含”张”)REPORT 用 addressbook-query
增量同步REPORT 用 sync-collection(RFC 6578)
改属性(显示名等)PROPPATCH

服务端通常用 ETag(单个 vCard 版本号)+ CTag/sync-token(整个通讯录变更令牌)做高效增量同步:客户端只拉”上次 token 之后变了的”,不用全量下载。

冲突处理靠 ETag 乐观锁:本地改了上传 PUT 时带旧 ETag,服务端若已变则返回 412,客户端再合并或提示冲突。


四、服务发现(不用背 URL)

按 RFC 6764,客户端拿到邮箱和密码后可以先试:

https://domain/.well-known/carddav

服务端返回 301/PROPFIND 跳转,再逐级 PROPFIND 出 principal → addressbook-home → 具体通讯录 URL。所以 iOS/macOS/Thunderbird 经常只填邮箱密码就能自动配好。


五、认证与安全


六、典型使用场景与客户端

一次手动 PUT 新建联系人的最小例子(curl):

curl -u user:app-pass -X PUT \
  https://carddav.example.com/carddav/user/contacts/alice.vcf \
  -H "Content-Type: text/vcard" \
  --data-binary $'BEGIN:VCARD\nVERSION:3.0\nFN:Alice Chen\nTEL;CELL:+86-138...\nEMAIL:alice@example.com\nEND:VCARD'

七、工程上容易踩的坑

  1. vCard 版本:服务端可能只收 3.0 或 4.0,照片/国际化字段在跨版本时丢属性。
  2. UID 稳定性:换手机重新导入 vCard 若没保留 UID,会被当成新人而不是更新。
  3. 大通讯录性能:纯 CTag 全量比对在万级联系人以上偏慢,要依赖 sync-collection 增量。
  4. Google 等特殊实现:不兼容完整 WebDAV(禁 LOCK/MOVE),客户端别依赖完整文件锁语义。
  5. 共享通讯录写冲突:多人同时改同一条,必须靠 ETag 乐观锁+客户端合并策略,协议本身不自动解冲突。

下面是 CardDAV 首次同步全流程时序图及配套说明。

时序图流程解读

整张图分三大阶段,对应三次”递进式发现”——这正是 CardDAV 区别于普通 REST API 的核心设计。

① 服务发现(RFC 6764)

客户端只需知道域名+账号密码,先访问 GET /.well-known/carddav,服务端返回 301 跳转到真实 principal 路径。这一步让用户无需记忆复杂 URL。

② 定位通讯录(PROPFIND 两轮)

每一轮都是”问 A 拿到 B 的地址,再向 B 发问”的链式发现,直到定位到具体通讯录集合。

③ 首次全量同步(REPORT sync-collection)

关键设计要点

机制作用
sync-token / CTag​整个通讯录的变更令牌,首次=0,之后增量
ETag​单条 vCard 版本号,PUT 更新时乐观锁防冲突
207 Multi-Status​WebDAV 批量响应格式,一次报告多个资源状态
UID 稳定性​vCard 的 UID 不变 = 同一个人,避免重复导入

⚠️ 工程提示:首次同步是全量的,万级联系人时会明显偏慢,这是协议固有特性;后续同步才走增量。生产环境建议在首次同步时加进度回调 + 失败断点续传(记录已拉取的 UID 集合)。

「二次增量同步」时序图。基于首次同步已保存 sync-token 的前提,增量同步的核心逻辑是:只拉取变更、ETag 冲突处理、token 滚动更新。。下面是 CardDAV 二次增量同步时序图及流程解读。

与首次同步的核心差异

首次同步是 sync-token=0 的全量下载;二次同步的本质变化在于:客户端已持有上次保存的 sync-token,因此整轮只传输「该 token 之后的变更」,数据量从 O(N) 降到 O(变更数)。

三阶段流程解读

① 先推本地变更(「先推后拉」原则)

成熟的同步实现都遵循这个顺序——先把本地未上传的改动 PUT 上去,再去拉服务端,否则会出现”刚拉完又被本地旧数据覆盖”的丢更新问题。

⚠️ 工程要点:合并策略要自己定(如”手机号取最新、备注取并集”),CardDAV 协议不自动解冲突,处理不当会导致互相覆盖。

② 拉取服务端增量(REPORT sync-collection)

③ Token 滚动更新

关键设计对比

维度首次同步二次增量同步
sync-token= 0(全量)= 上次保存值(仅变更)
数据传输所有 vCard新增/修改/删除的差量
上传方向通常只读先推本地 → 再拉远端​
冲突处理不涉及ETag 412 → GET 合并 → 重 PUT​
Token 处理同步后保存每次滚动更新​

完整循环

至此,同步模型已闭环:① 推本地 → ② 拉增量 → ③ 更新 token,此后每次打开通讯录都重复这三步,全程只传输变更。



上一篇
CalDAV同步过程中如何处理时区差异问题?
下一篇
详解 WebDAV