摘要

  • RFC 9110 把 method token 定为请求语义的首要来源:它说明客户端为何发出请求、何种结果算成功;目标资源仍独立决定是否识别、实现或允许这种方法。
  • Safe 约束的是客户端请求的语义,不保证服务器没有任何副作用;Idempotent 约束重复请求的预期效果,不保证响应、日志或全部外部结果完全相同。
  • 一次敏感操作的完整凭证必须同时记录方法、目标、认证主体、授权政策、状态前置条件、首次尝试是否落地的不确定性、应用级幂等证据、重试者、响应、最终状态与外部副作用。

若只看请求第一行,方法最抢眼。GET、PUT、DELETE 都是动词,像是在直接驱动服务器。但标准真正给它们的权力更小,也更精确:它们把客户端的意图变成公共语义,使不属于同一应用的组件能够作出有限判断。

有限判断不是最终裁决。服务器可以认识 PUT,却没有为某资源实现它;可以已经实现,却不允许该资源使用;可以允许资源使用,却拒绝当前主体;可以授权主体,却发现其 ETag 已过期;还可能已经完成写入,只是响应在网络中丢失。把这些结果都压缩为“这是 PUT”会让系统失去解释自身行为的能力。

统一接口公开的是意图

RFC 9110 说,method token 表明客户端发出请求的目的,以及客户端期待的成功结果。GET 请求目标资源的当前表示。PUT 请求在明确目标上创建或替换相应状态。DELETE 请求移除目标 URI 与其当前功能的关联;它不承诺清除所有存储副本,也不承诺立即回收空间。

标准方法应当在不同资源上保持同样的语义。因此,缓存能区分读取与修改,爬虫能识别安全方法,客户端能在连接断裂后判断是否考虑重试。条件请求可以进一步限定方法,例如要求目标仍处于客户端曾经看到的版本,但附加语义不能反过来改变方法的基本含义。

这种统一性停在资源门口。资源自己决定是否实现或允许方法。服务器不认识或没有实现时应返回 501;服务器认识且已经实现,但目标资源不支持时应返回 405。405 还要携带 Allow,列出目标当前支持的方法,而这份列表可以动态变化。

Allow 不是访问控制表。它能证明“这个资源具有这种操作面”,不能证明“这个人可以操作”。认证回答主体是谁;授权把主体、方法、目标与政策相连;方法支持只回答资源是否具备这种标准能力。三者必须分别留痕。

这也符合 Heng Lu 的 Minimum Initial Specification:公共层只严格规定互操作真正需要的语义,之后的业务选择留给运行资源的参与者。IANA 方法登记表是一份公共词汇账本,不是一台中央权限服务器。

Safe 划分责任,而不是抹掉副作用

安全方法的定义语义基本只读:客户端没有请求、也不期待源站状态发生变化。RFC 9110 随即说明,服务器仍可能写访问日志,广告点击仍可能产生计费,具体实现还可能执行其他带副作用的工作。关键在于这些行为不是客户端请求的,因此不能把它们的责任倒推给客户端。

这个边界让链接检查、索引与预取可以成立,也给资源所有者明确义务。如果 page?do=delete 在 GET 下真的删除内容,错误属于资源设计者。把“delete”藏进查询参数,并不会把安全方法变成客户端主动请求的危险操作。爬虫遍历所有 URI 时,隐藏命令会变成真实事故。

审计时要分两列:客户端请求了什么状态变化;服务器自行附加了什么效果。日志写入、扣费、配额消耗、追踪与对第三方的通知都属于第二列。只有这样,平台才不能借 Safe 之名把自己的选择伪装成客户端意图。

Safe 也不等于公开可读。一份机密资料可以通过 GET 获取,方法语义仍然安全,但主体必须先获权。只读泄露照样是安全事件。方法属性指导自动化,授权系统保护边界。

Idempotent 保证预期效果收敛,不复制整个世界

若多次相同请求在服务器上的预期效果等同于一次,该方法就是幂等的。RFC 9110 将 PUT、DELETE 与安全方法列为幂等。服务器仍可以为每次尝试写一条日志、保存多个修订记录,或者返回不同响应。

第一次 DELETE 成功后,第二次可能发现资源已不存在。两次响应不同,但目标状态相同。重复 PUT 也可能得到不同日期或验证器,只要替换后的预期状态保持一致,方法层面的幂等仍成立。

应用实现可能破坏这份公共承诺。若一个名为 PUT 的接口实际执行“余额加一”,重复就会累加。若每次 DELETE 都向外部系统重新发送不可逆命令,本地 URI 状态可能已经收敛,外部损害却在增加。

所以,方法属性之外还需要应用级 operation key、去重记录、commit 状态与补偿机制。每一个下游副作用都应有自己的幂等边界。HTTP 的幂等语义不是分布式事务保证,只是让参与者知道该检查什么。

重试面对的是第一次尝试是否成功的未知

连接在响应返回前断开时,客户端可能不知道请求有没有落地。请求也许未到服务器,也许已经提交,也许只是响应丢了。幂等方法适合重试,是因为第二次尝试即使遇到已经成功的第一次,也应达到同一个预期效果。

它不允许无限循环。RFC 9110 要求:除非客户端知道该资源上的具体操作实际幂等,或者能确定原请求未被执行,否则不应自动重试非幂等方法;代理不得自动重试非幂等请求;一次自动重试再次失败后,客户端也不应继续自动重复。

真正的重试决定需要更多事实:目标、内容哈希、断点位置、前置条件、应用 operation key、可查询状态、尝试次数与退避。带稳定业务键的 POST 可能安全恢复;带多重外部效果而无去重的 PUT 可能非常危险。

Running-Code Primacy 在这里提供了实用检验:不要只看登记表上的标签,要验证重复请求是否真实收敛、提交结果能否独立查询、外部效果是否受限。能被运行者本地核验的行为,才是可靠证据。

If-Match 保护版本,不证明主体

If-Match 可以要求资源当前的 ETag 仍与客户端持有的值一致,才执行修改。条件失败时,服务器不得执行所请求的方法,通常返回 412。这样可以避免编辑者用旧副本覆盖其他人刚完成的变更。

ETag 不是凭证。知道它,不等于有写入权;已经有写入权,也不能让过期 ETag 重新有效。授权处理“这个主体能不能做”,前置条件处理“当前状态是不是请求所假定的状态”。两种拒绝必须保留各自原因。

响应丢失后,资源版本还能帮助判断第一次是否已完成。RFC 9110 在有限情况下允许识别“请求其实已经成功”,同时提醒:多个未协调主体提交相似修改时,过度推断会丢失状态转换。资源模型决定可恢复程度。

QUERY 让原本私有的知识成为公共语义

2026 年 6 月发布的 RFC 10008 定义 QUERY,作者是 Julian Reschke、James Snell 与 Mike Bishop,不是 Fielding。QUERY 像 POST 一样携带请求内容,却明确声明一种安全、幂等的查询。IANA 方法登记表把它的 Safe 与 Idempotent 都记为 yes。

它解决的是可见性问题。复杂查询放进 URI 可能过长,也可能进入日志、历史与书签,所以很多应用使用 POST 读取数据。但对通用代理或缓存而言,一个 POST 是否只读、能否重试,若没有应用私有知识就无法判断。QUERY 把这部分意图公开,同时仍由目标资源定义查询内容并决定是否支持。

登记不等于部署。网关可能不转发,服务器可能返回 501,资源可能返回 405,主体也可能无权查询。公共登记的作用,是在参与者自愿采用时提供一致含义,而不是替所有节点作决定。

Fielding 的贡献是一条接口边界

截至 2026 年 8 月 31 日,IETF Datatracker 将 Roy T. Fielding 介绍为 Adobe Senior Principal Scientist、The Apache Software Foundation 联合创始人、REST 架构风格作者,以及 HTTP、URI、URI Templates 的标准贡献者;资料列有 18 份 RFC 与 HTTP Directorate 审查职责。UC Irvine 记录了他的学位以及 Web、REST、Apache 贡献。

他的博士论文把统一接口视为 REST 约束:它牺牲部分面向单一应用的效率,换取交互可见性、通用组件复用、可扩展性与独立演进。方法词汇正是这种交换的实例。公共层暴露足够的意图,却不需要了解资源内部如何存储、授权与执行。

归属边界同样重要。RFC 9110 有三位编辑:Fielding、Mark Nottingham 与 Julian Reschke。HTTP 是长期集体工作,具体实现拥有自己的选择。RFC 10008 应归于其列名作者。Fielding 的署名使贡献可追溯,并不赋予他对 HTTP 或运行中资源的所有权。

用操作凭证替代“看动词就知道”的捷径

敏感操作先记录方法、目标 URI 与源站,再记录认证主体、credential scope、资源支持判断、授权政策及版本、ETag 或其他前置条件、请求内容哈希与预期效果。

发生断连后,补记最后确认的传输点、首次尝试可能已经落地的概率、应用 operation key、重试发起者、触发原因、次数与退避。最后读取资源状态,并记录响应、下游效果、补偿以及不可逆后果。

这样,每个问题都有自己的证据:方法说明客户端想问什么;资源说明是否理解;授权说明该主体能否这样问;前置条件说明状态是否仍然有效;重试记录说明是否值得再试;最终观察说明实际发生了什么。

method token 能命名意图,但不能授予权限。统一接口的力量,正来自它没有越过这条线。

来源