跳到主要内容

2. PATCH 方法 (The PATCH Method)

2. PATCH 方法 (The PATCH Method)

PATCH 方法请求将请求实体中描述的一组更改应用到 Request-URI 所标识的资源. 这组更改以一种称为 "补丁文档 (patch document)" 的格式表示, 该格式由媒体类型标识. 如果 Request-URI 未指向现有资源, 服务器可以创建一个新资源, 具体取决于补丁文档类型 (即它是否可以在逻辑上修改空资源) 以及权限等因素.

PUT 和 PATCH 请求之间的区别体现在服务器处理所含实体以修改 Request-URI 所标识资源的方式上. 在 PUT 请求中, 所含实体被视为源服务器上所存储资源的一个修改后版本, 客户端请求用它替换已存储的版本. 而使用 PATCH 时, 所含实体包含一组指令, 描述应如何修改当前位于源服务器上的资源以生成新版本. PATCH 方法会影响 Request-URI 所标识的资源, 并且可以对其他资源产生副作用; 也就是说, 应用一次 PATCH 可能会创建新资源, 或修改现有资源.

按照 [RFC2616] 第 9.1 节的定义, PATCH 既不是安全的 (safe), 也不是幂等的 (idempotent).

PATCH 请求可以按具备幂等性的方式发出, 这也有助于避免在相近时间内针对同一资源的两个 PATCH 请求发生冲突而造成不良结果. 多个 PATCH 请求之间的冲突可能比 PUT 冲突更危险, 因为某些补丁格式需要基于已知基点操作, 否则会损坏资源. 使用这类补丁应用的客户端应该使用条件请求, 使得如果资源自客户端上次访问后已被更新, 该请求就会失败. 例如, 客户端可以在 PATCH 请求的 If-Match header 中使用强 ETag [RFC2616].

也存在补丁格式不需要基于已知基点操作的情况, 例如向日志文件追加文本行, 或向数据库表追加不会冲突的行. 在这种情况下, 客户端请求不需要同等程度的谨慎.

服务器必须以原子方式应用整组更改, 并且绝不能提供部分修改后的表示, 例如在此操作期间响应 GET 时提供这样的表示. 如果整个补丁文档不能成功应用, 则服务器不得应用其中任何更改. 如何判定一次 PATCH 成功, 可能会随补丁文档以及被修改资源的类型而变化. 例如, 常见的 'diff' 工具可以生成一个适用于目录层级中多个文件的补丁文档. 原子性要求适用于受该补丁影响的所有文件, 而不只是其中一个文件. 有关状态码和可能错误条件的详细信息, 见 "错误处理 (Error Handling)" 第 2.2 节.

如果请求经过缓存, 且 Request-URI 标识了一个或多个当前已缓存的实体, 这些条目应该被视为陈旧. 对此方法的响应只有在同时包含明确的新鲜度信息 (例如 Expires header 或 "Cache-Control: max-age" 指令) 以及与 Request-URI 匹配的 Content-Location header 时才可缓存, 这表示 PATCH 响应正文是一个资源表示. 已缓存的 PATCH 响应只能用于响应后续 GET 和 HEAD 请求; 它不得用于响应其他方法, 例如 PATCH.

请注意, 请求中包含的实体头部仅适用于其中的补丁文档, 不得应用于正在修改的资源. 因此, 请求中可以存在 Content-Language header, 但它只表示该补丁文档具有某种语言, 无论这有多大意义. 除作为跟踪信息外, 服务器不应该存储这类 header, 并且不应该以可能用于 PUT 请求的方式使用这些 header 值. 因此, 本文档不规定通过 header 修改文档 Content-Type 或 Content-Language 值的方式, 尽管完全可以设计一种通过补丁文档达成此目标的机制.

不能保证某个资源一定可以用 PATCH 修改. 此外, 不同的补丁文档格式预计会适用于不同类型的资源, 且不存在一种适用于所有资源类型的单一格式. 因此, 不存在要求实现必须支持的单一默认补丁文档格式. 服务器必须确保收到的补丁文档适用于目标 Request-URI 所标识资源的类型.

客户端需要判断何时使用 PATCH 而不是 PUT. 例如, 如果补丁文档的大小大于 PUT 将使用的新资源数据大小, 那么使用 PUT 而不是 PATCH 可能更合理. 与 POST 的比较则更困难, 因为 POST 的用法差异很大, 并且如果服务器选择这样做, POST 可以涵盖类似 PUT 和 PATCH 的操作. 如果某个操作不会以可预测方式修改 Request-URI 所标识的资源, 则应考虑使用 POST, 而不是 PATCH 或 PUT.