7. Write Lock (写锁)
本节描述写锁类型 (Write Lock Type) 的具体语义。写锁是锁类型的一个具体实例,也是本规范中描述的唯一锁类型。
排他写锁 (Exclusive Write Lock) 保护资源:它防止除锁创建者之外的任何主体进行更改,以及在任何未提交锁令牌的情况下进行更改(例如,由持有锁的进程以外的客户端进程)。
客户端必须 (MUST) 在修改写锁定资源的任何请求中提交其有权使用的锁令牌。写锁涵盖的修改列表包括:
受写锁保护的修改
1. 对任何写锁定资源的以下方面的更改:
- 任何变体 (Variant)
- 任何死属性 (Dead Property)
- 任何可锁定的活属性 (Live Property)(除非另有定义,否则活属性是可锁定的)
2. 对于集合,任何内部成员URI的修改:
- 如果添加、删除内部成员URI,或者它标识不同的资源,则认为内部成员URI被修改
- 有关写锁和集合的更多讨论,请参见第7.4节
3. 写锁根的映射修改:
- 映射到另一个资源
- 映射到无资源(例如DELETE)
在HTTP和WebDAV中定义的方法中,受写锁影响的方法有:PUT、POST、PROPPATCH、LOCK、UNLOCK、MOVE、COPY(对于目标资源)、DELETE和MKCOL。所有其他迄今定义的HTTP/WebDAV方法——特别是GET——独立于写锁运行。
7.1 Write Locks and Properties (写锁和属性)
虽然没有写锁的用户不能更改资源上的属性,但活属性的值仍然可能会改变,即使在锁定时,这是由于其模式的要求。只有死属性和定义为可锁定的活属性才能保证在写锁定时不会改变。
7.2 Avoiding Lost Updates (避免更新丢失)
尽管写锁在防止更新丢失方面提供了一些帮助,但它们无法保证更新永远不会丢失。考虑以下场景:
更新丢失场景示例:
- 客户端A和B都对编辑资源'index.html'感兴趣
- 客户端A是HTTP客户端而不是WebDAV客户端,因此不知道如何执行锁定
- 客户端A不锁定文档,但执行GET并开始编辑
- 客户端B执行LOCK,执行GET并开始编辑
- 客户端B完成编辑,执行PUT,然后执行UNLOCK
- 客户端A执行PUT,覆盖并丢失B的所有更改
为什么WebDAV无法完全防止这种情况:
- HTTP兼容性: 无法强制所有客户端使用锁定,因为必须与不理解锁定的HTTP客户端兼容
- 服务器实现多样性: 无法要求服务器支持锁定,因为存储库实现多种多样,有些依赖于保留和合并而不是锁定
- 无状态性: 作为无状态协议,无法强制执行像LOCK / GET / PUT / UNLOCK这样的操作序列
最佳实践:
- 服务器端: 支持锁定的WebDAV服务器可以通过要求客户端在修改资源之前锁定资源来降低客户端意外覆盖彼此更改的可能性
- WebDAV客户端: 应该使用"锁定/检索/写入/解锁"的操作序列(至少默认情况下),每当与支持锁定的WebDAV服务器交互时
- HTTP 1.1客户端: 可以通过在任何修改资源的请求中使用If-Match头中的实体标签来避免覆盖其他客户端的更改
- 信息管理员: 可以通过实施客户端程序来尝试防止覆盖,要求在修改WebDAV资源之前进行锁定
7.3 Write Locks and Unmapped URLs (写锁和未映射URL)
WebDAV提供了向未映射URL发送LOCK请求的能力,以便保留该名称供使用。这是避免在创建新资源时出现更新丢失问题的一种简单方法(另一种方法是使用[RFC2616]第14.26节中指定的If-None-Match头)。它有一个附带好处,即立即锁定新资源供创建者使用。
对未映射URL的成功锁定请求必须 (MUST) 导致创建一个具有空内容的锁定(非集合)资源。随后,成功的PUT请求(带有正确的锁令牌)为资源提供内容。
锁定空资源的行为:
使用LOCK创建的资源是空的,但在其他方面的行为与普通资源完全相同:
✅ 可以执行的操作:
- 可以读取、删除、移动和复制
- 在各方面表现为常规的非集合资源
- 显示为其父集合的成员
- 可以使用PUT请求更新(添加内容)
✅ 属性行为:
- 应该 (SHOULD NOT) 在锁消失时消失
- 可能 (MAY NOT) 具有诸如
DAV:getcontentlanguage之类尚未由客户端指定的属性值 - 必须 (MUST) 具有
DAV:lockdiscovery和DAV:supportedlock属性的定义值
❌ 不能执行的操作:
- 禁止 (MUST NOT) 转换为集合
- 服务器必须 (MUST) 拒绝MKCOL请求(就像对任何现有的非集合资源的MKCOL请求一样)
响应要求:
- 响应必须 (MUST) 使用"201 Created"响应码指示资源已创建
- 主体必须 (MUST) 仍然包含
DAV:lockdiscovery属性
向后兼容性: 为了与[RFC2518]向后兼容,服务器可以 (MAY) 实现Lock-Null Resources (LNR)(参见附录D中的定义)。客户端可以轻松地与支持旧模型LNR的服务器和推荐的"锁定空资源"模型的服务器互操作,方法是仅在对未映射URL的LOCK之后尝试PUT,而不是MKCOL或GET,并且不依赖LNR的特定属性。
7.4 Write Locks and Collections (写锁和集合)
有两种集合写锁:
Depth-0写锁:
- 保护集合属性以及该集合的内部成员URL
- 不保护成员资源的内容或属性
- 如果集合本身有任何实体主体,这些也受到保护
Depth-infinity写锁:
- 对该集合提供相同的保护
- 还对每个成员资源提供写锁保护
换句话说,任何一种写锁都保护:
- 在写锁定集合中创建新资源的任何请求
- 删除写锁定集合的内部成员URL的任何请求
- 更改任何内部成员的段名称的任何请求
7.4.1 集合写锁的保护范围
被保护的操作:
- 在锁定集合中添加或删除成员
- 重命名集合中的成员
- 修改集合的属性
不被保护的操作(Depth-0锁):
- 修改成员资源的内容
- 修改成员资源的属性
7.5 Write Locks and the If Request Header (写锁和If请求头)
If请求头(第10.4节)用于提交锁令牌。在对写锁定资源执行修改操作时,客户端必须在If头中提交适当的锁令牌。
7.6 Write Locks and COPY/MOVE (写锁和COPY/MOVE)
COPY操作:
- 源资源的锁不会复制到目标资源
- 如果目标资源已锁定,需要提交目标资源的锁令牌才能覆盖
MOVE操作:
- MOVE是原子操作
- 源资源的锁会随资源一起移动到新位置
- 移动后,锁令牌保持不变,但锁根URL会更新
7.7 Refreshing Write Locks (刷新写锁)
客户端可以通过向锁定资源发送LOCK请求来刷新写锁,请求中包含现有的锁令牌。刷新锁会重置超时计数器。
刷新锁的最佳实践:
- 客户端应该 (SHOULD) 在锁超时之前刷新长期锁
- 服务器必须 (MUST) 在刷新锁的响应中返回新的超时值
- 刷新锁不会改变锁的其他属性(类型、作用域等)