跳到主要内容

4. 请求方法

4.1. 概述

请求方法令牌是请求语义的主要来源; 它指明客户端发出此请求的目的, 以及客户端期望什么作为成功结果.

当某些头部字段出现在请求中时 (Section 5), 只要这些附加语义不与方法冲突, 请求方法的语义可以由这些头部字段的语义进一步专门化. 例如, 客户端可以发送条件请求头部字段 (Section 5.2), 使所请求的动作取决于目标资源的当前状态 ([RFC7232]).

method = token

HTTP 最初被设计为可用作分布式对象系统的接口. 当时设想请求方法向目标资源应用语义, 很像在已标识对象上调用一个已定义方法会应用语义. 方法令牌区分大小写, 因为它可能被用作通向基于对象且方法名区分大小写的系统的网关.

与分布式对象不同, HTTP 中的标准化请求方法不是资源特定的, 因为统一接口能在基于网络的系统中提供更好的可见性和复用性 [REST]. 一旦定义, 标准化方法在应用于任何资源时都应具有相同语义, 但每个资源自行决定是否实现或允许这些语义.

本规范定义了一组 HTTP 中常用的标准化方法, 如下表所示. 按约定, 标准化方法使用全大写 US-ASCII 字母定义.

MethodSafeIdempotentDescription
GETYesYes传输目标资源的当前表示.
HEADYesYes与 GET 相同, 但只传输状态行和头部节.
POSTNoNo对请求负载执行资源特定的处理.
PUTNoYes用请求负载替换目标资源的所有当前表示.
DELETENoYes移除目标资源的所有当前表示.
CONNECTNoNo建立到由目标资源标识的服务器的隧道.
OPTIONSYesYes描述目标资源的通信选项.
TRACEYesYes沿到目标资源的路径执行消息回环测试.

所有通用服务器 MUST 支持 GET 和 HEAD 方法. 所有其他方法都是 OPTIONAL.

本规范范围之外的其他方法也已被标准化用于 HTTP. 所有这些方法都应注册到 IANA 维护的 "Hypertext Transfer Protocol (HTTP) Method Registry" 中, 如 Section 8.1 所定义.

目标资源允许的方法集合可以列在 Allow 头部字段中 (Section 7.4.1). 不过, 允许的方法集合可以动态变化. 当源服务器收到无法识别或未实现的请求方法时, 源服务器 SHOULD 以 501 (Not Implemented) 状态码响应. 当源服务器收到它已知但目标资源不允许的请求方法时, 源服务器 SHOULD 以 405 (Method Not Allowed) 状态码响应.

4.2. 通用方法属性

4.2.1. 安全方法

如果请求方法所定义的语义本质上是只读的, 则该方法被认为是"安全的"; 即, 客户端在把安全方法应用于目标资源时, 既不请求也不期望源服务器发生任何状态改变. 同样, 对安全方法的合理使用预期不会对源服务器造成任何损害、财产损失或异常负担.

安全方法的这个定义并不阻止实现包含潜在有害、并非完全只读、或在调用安全方法时产生副作用的行为. 但重要的是, 客户端没有请求这种附加行为, 也不能为其负责. 例如, 大多数服务器会在每个响应完成时把请求信息追加到访问日志文件, 而不管使用何种方法; 即使日志存储可能被写满并导致服务器崩溃, 这仍被认为是安全的. 同样, 通过在 Web 上选择广告而发起的安全请求, 往往会产生向广告账户计费的副作用.

在本规范定义的请求方法中, GET、HEAD、OPTIONS, 和 TRACE 方法被定义为安全的.

区分安全方法和不安全方法的目的, 是让自动化检索过程 (spiders) 和缓存性能优化 (pre-fetching) 能够在不担心造成损害的情况下工作. 此外, 在处理可能不可信的内容时, 它允许用户代理对不安全方法的自动化使用施加适当约束.

用户代理在向用户呈现可能的动作时 SHOULD 区分安全方法和不安全方法, 以便用户能在请求不安全动作之前知晓这一点.

当资源被构造成有效请求 URI 中的参数具有选择动作的效果时, 资源所有者有责任确保该动作与请求方法语义一致. 例如, 基于 Web 的内容编辑软件常常在查询参数中使用动作, 如 "page?do=delete". 如果这类资源的目的是执行不安全动作, 则资源所有者 MUST 在使用安全请求方法访问该资源时禁用或禁止该动作. 如果不这样做, 当自动化过程为了链接维护、预取、构建搜索索引等目的对每个 URI 引用执行 GET 时, 将导致不良副作用.

4.2.2. 幂等方法

如果使用某个请求方法发出多个相同请求时对服务器产生的预期效果, 与发出单个这样的请求所产生的效果相同, 则该请求方法被认为是"幂等的". 在本规范定义的请求方法中, PUT、DELETE, 以及安全请求方法都是幂等的.

与安全性的定义类似, 幂等属性只适用于用户所请求的内容; 服务器可以自由地分别记录每个请求、保留修订控制历史, 或为每个幂等请求实现其他非幂等副作用.

幂等方法之所以被区分出来, 是因为如果客户端在能够读取服务器响应之前发生通信故障, 请求可以自动重复. 例如, 如果客户端发送 PUT 请求, 而底层连接在收到任何响应之前关闭, 则客户端可以建立新连接并重试该幂等请求. 它知道重复该请求会有相同的预期效果, 即使原始请求已经成功, 尽管响应可能不同.

4.2.3. 可缓存方法

请求方法可以被定义为"可缓存的", 表示对它们的响应允许被存储以供将来复用; 具体要求见 [RFC7234]. 通常, 不依赖当前或权威响应的安全方法被定义为可缓存; 本规范将 GET、HEAD, 和 POST 定义为可缓存, 尽管绝大多数缓存实现只支持 GET 和 HEAD.

4.3. 方法定义

4.3.1. GET

GET 方法请求传输目标资源的当前选定表示. GET 是信息检索的主要机制, 也是几乎所有性能优化的焦点. 因此, 当人们谈论通过 HTTP 检索某些可标识信息时, 通常指的是发出 GET 请求.

把资源标识符想象成远程文件系统路径名, 把表示想象成这些文件内容的副本, 这种想法很诱人, 但并不正确. 对 GET 返回的表示不一定只是源服务器存储的资源内容. 相反, 它们是根据请求被选择 (通过内容协商) 并即时生成的.

GET 请求消息中的负载没有已定义语义; 在 GET 请求中发送负载体可能导致某些现有实现拒绝该请求.

GET 请求的响应是可缓存的; 除非 Cache-Control 头部字段另有指示 ([RFC7234] Section 5.2), 缓存 MAY 使用它满足后续 GET 和 HEAD 请求.

4.3.2. HEAD

HEAD 方法与 GET 相同, 但服务器 MUST NOT 在响应中发送消息体 (即, 响应在头部节末尾终止). 服务器 SHOULD 在对 HEAD 请求的响应中发送与该请求若为 GET 时本会发送的相同头部字段, 但 MAY 省略负载头部字段 (Section 3.3). 该方法可用于在不传输表示数据的情况下获取有关选定表示的元数据, 并常用于测试超文本链接的有效性、可访问性和最近修改情况.

HEAD 请求消息中的负载没有已定义语义; 在 HEAD 请求中发送负载体可能导致某些现有实现拒绝该请求.

HEAD 请求的响应是可缓存的; 除非 Cache-Control 头部字段另有指示 ([RFC7234] Section 5.2), 缓存 MAY 使用它满足后续 HEAD 请求. HEAD 响应也可能影响先前缓存的 GET 响应; 见 [RFC7234] Section 4.3.5.

4.3.3. POST

POST 方法请求目标资源按照该资源自身的特定语义处理请求中封装的表示. 例如, POST 用于以下功能 (以及其他功能):

  • 向数据处理过程提供一块数据, 如输入到 HTML 表单中的字段;

  • 向公告板、新闻组、邮件列表、博客或类似文章组发布消息;

  • 创建尚未由源服务器标识的新资源; 以及,

  • 向资源的现有表示追加数据.

源服务器通过根据 POST 请求处理结果选择适当的状态码来指示响应语义; 在对 POST 的响应中, 几乎可能收到本规范定义的所有状态码 (例外是 206 (Partial Content)、304 (Not Modified), 和 416 (Range Not Satisfiable)).

如果成功处理 POST 请求导致在源服务器上创建了一个或多个资源, 源服务器 SHOULD 发送 201 (Created) 响应, 其中包含一个 Location 头部字段来提供所创建主资源的标识符 (Section 7.1.2), 并包含一个描述请求状态且引用新资源的表示.

只有当 POST 请求的响应包含显式新鲜度信息时, 这些响应才是可缓存的 (见 [RFC7234] Section 4.2.1). 不过, POST 缓存并未被广泛实现. 对于源服务器希望客户端能够以日后 GET 可复用的方式缓存 POST 结果的情况, 源服务器 MAY 发送包含该结果的 200 (OK) 响应, 并发送一个 Content-Location 头部字段, 其值与 POST 的有效请求 URI 相同 (Section 3.1.4.2).

如果处理 POST 的结果等价于某个现有资源的表示, 源服务器 MAY 通过发送 303 (See Other) 响应并在 Location 字段中放入该现有资源的标识符, 将用户代理重定向到该资源. 这样做的好处是向用户代理提供资源标识符, 并通过更适合共享缓存的方法传输表示; 代价是如果用户代理尚未缓存该表示, 则需要额外一次请求.

4.3.4. PUT

PUT 方法请求用请求消息负载中封装的表示所定义的状态来创建或替换目标资源的状态. 对给定表示成功执行 PUT 表明, 后续对同一目标资源执行 GET 将导致在 200 (OK) 响应中发送一个等价表示. 不过, 并不能保证这种状态改变一定可被观察到, 因为在收到任何后续 GET 之前, 目标资源可能被其他用户代理并行操作, 或可能受到源服务器的动态处理. 成功响应只意味着用户代理的意图在源服务器处理该请求时已经达成.

如果目标资源没有当前表示, 且 PUT 成功创建了一个表示, 则源服务器 MUST 通过发送 201 (Created) 响应告知用户代理. 如果目标资源已有当前表示, 且该表示已按封装表示的状态成功修改, 则源服务器 MUST 发送 200 (OK) 或 204 (No Content) 响应以指示请求已成功完成.

源服务器 SHOULD 验证 PUT 表示是否与服务器对目标资源施加的、不能由 PUT 改变或未由 PUT 改变的任何约束一致. 当源服务器使用与 URI 相关的内部配置信息来设置 GET 响应中的表示元数据值时, 这一点尤其重要. 当 PUT 表示与目标资源不一致时, 源服务器 SHOULD 通过转换表示或更改资源配置使二者一致, 或者以适当错误消息响应, 并包含足够信息说明该表示为何不适合. 建议使用 409 (Conflict) 或 415 (Unsupported Media Type) 状态码, 后者专用于对 Content-Type 值的约束.

例如, 如果目标资源被配置为始终具有 Content-Type "text/html", 而正在 PUT 的表示具有 Content-Type "image/jpeg", 源服务器应执行以下之一:

a. 重新配置目标资源以反映新的媒体类型;

b. 在保存为新的资源状态之前, 将 PUT 表示转换为与该资源一致的格式; 或,

c. 以 415 (Unsupported Media Type) 响应拒绝请求, 指明目标资源限于 "text/html", 也许还包含一个指向另一资源的链接, 该资源适合作为新表示的目标.

除了可由用户代理请求意图和源服务器响应语义表达的内容之外, HTTP 并不精确定义 PUT 方法如何影响源服务器状态. 除了通过 HTTP 提供的接口之外, 它不以任何意义定义资源可能是什么. 它不定义资源状态如何被"存储", 也不定义这种存储会如何因资源状态改变而变化, 或源服务器如何把资源状态转换为表示. 一般而言, 资源接口背后的所有实现细节都被服务器有意隐藏.

源服务器 MUST NOT 在对 PUT 的成功响应中发送验证器头部字段 (Section 7.2), 如 ETagLast-Modified 字段, 除非请求的表示数据在未对主体应用任何转换的情况下被保存 (即, 资源的新表示数据与 PUT 请求中收到的表示数据相同), 且验证器字段值反映新表示. 该要求让用户代理能够知道, 由于 PUT 的结果, 它内存中的表示体仍然是当前的, 因此不需要再次从源服务器检索; 同时, 响应中收到的新验证器可用于将来的条件请求, 以防止意外覆盖 (Section 5.2).

POST 和 PUT 方法之间的根本差异体现在对封装表示的不同意图上. POST 请求中的目标资源旨在按照资源自身语义处理封装表示, 而 PUT 请求中的封装表示被定义为替换目标资源的状态. 因此, PUT 的意图是幂等的, 并且对中间方可见, 尽管确切效果只有源服务器知道.

正确解释 PUT 请求预设用户代理知道期望的目标资源是哪一个. 如果某个服务在收到状态改变请求后代表客户端选择合适的 URI, 则 SHOULD 使用 POST 方法而不是 PUT 来实现. 如果源服务器不会对目标资源执行所请求的 PUT 状态改变, 而是希望将其应用到另一个资源, 例如资源已移动到不同 URI 时, 则源服务器 MUST 发送适当的 3xx (Redirection) 响应; 用户代理 MAY 随后自行决定是否重定向该请求.

应用于目标资源的 PUT 请求可能对其他资源产生副作用. 例如, 一篇文章可能有一个用于标识"当前版本"的 URI (一个资源), 它不同于标识每个特定版本的 URI (不同资源, 它们曾在某一时刻与当前版本资源共享相同状态). 因此, 对"当前版本" URI 的成功 PUT 请求可能除了改变目标资源状态之外, 还会创建一个新版本资源, 并可能导致在相关资源之间添加链接.

允许对给定目标资源执行 PUT 的源服务器, 对包含 Content-Range 头部字段 ([RFC7233] Section 4.2) 的 PUT 请求 MUST 发送 400 (Bad Request) 响应, 因为该负载很可能是被错误地当作完整表示 PUT 的部分内容. 可以通过把目标指向一个单独标识的资源来进行部分内容更新, 该资源的状态与较大资源的一部分重叠; 或者使用另一个专门为部分更新定义的方法 (例如 [RFC5789] 中定义的 PATCH 方法).

PUT 方法的响应不可缓存. 如果成功的 PUT 请求经过一个缓存, 而该缓存为有效请求 URI 存有一个或多个响应, 这些已存储响应将被失效 (见 [RFC7234] Section 4.4).

4.3.5. DELETE

DELETE 方法请求源服务器移除目标资源与其当前功能之间的关联. 实际上, 该方法类似于 UNIX 中的 rm 命令: 它表达的是对源服务器 URI 映射的删除操作, 而不是期望先前关联的信息本身被删除.

如果目标资源有一个或多个当前表示, 它们可能会也可能不会被源服务器销毁, 相关存储也可能会或不会被回收, 这完全取决于资源的性质及源服务器对它的实现 (这些都超出本规范范围). 同样, DELETE 的结果可能需要停用或归档资源的其他实现方面, 如数据库或网关连接. 一般而言, 假定源服务器只会允许对其已有规定机制来完成删除的资源执行 DELETE.

相对而言, 允许 DELETE 方法的资源并不多 -- 它主要用于远程创作环境, 在这些环境中用户对其效果有一定指示. 例如, 先前使用 PUT 请求创建的资源, 或在对 POST 请求的 201 (Created) 响应之后通过 Location 头部字段标识的资源, 可能允许相应的 DELETE 请求撤销这些动作. 类似地, 实现创作功能的自定义用户代理实现, 如使用 HTTP 进行远程操作的版本控制客户端, 可能基于服务器 URI 空间被设计为对应版本仓库这一假设来使用 DELETE.

如果 DELETE 方法成功应用, 当动作很可能成功但尚未执行时, 源服务器 SHOULD 发送 202 (Accepted) 状态码; 当动作已经执行且无需提供进一步信息时, SHOULD 发送 204 (No Content) 状态码; 当动作已经执行且响应消息包含描述状态的表示时, SHOULD 发送 200 (OK) 状态码.

DELETE 请求消息中的负载没有已定义语义; 在 DELETE 请求中发送负载体可能导致某些现有实现拒绝该请求.

DELETE 方法的响应不可缓存. 如果 DELETE 请求经过一个缓存, 而该缓存为有效请求 URI 存有一个或多个响应, 这些已存储响应将被失效 (见 [RFC7234] Section 4.4).

4.3.6. CONNECT

CONNECT 方法请求接收者建立一条到由 request-target 标识的目标源服务器的隧道; 如果成功, 此后接收者的行为将限制为在两个方向上盲转发数据包, 直到隧道关闭. 隧道通常用于通过一个或多个代理创建端到端虚拟连接, 随后可使用 TLS (Transport Layer Security, [RFC5246]) 保护该连接.

CONNECT 仅预期用于发往代理的请求. 收到发给自身的 CONNECT 请求的源服务器 MAY 以 2xx (Successful) 状态码响应, 表示连接已建立. 不过, 大多数源服务器不实现 CONNECT.

发送 CONNECT 请求的客户端 MUST 发送 authority 形式的 request-target ([RFC7230] Section 5.3); 即, request-target 只由隧道目标的主机名和端口号组成, 二者由冒号分隔. 例如,

CONNECT server.example.com:80 HTTP/1.1
Host: server.example.com:80

接收代理可以通过直接连接 request-target 来建立隧道, 或者如果它被配置为使用另一个代理, 则把 CONNECT 请求转发给下一个入站代理. 任何 2xx (Successful) 响应都表示发送者 (以及所有入站代理) 将在结束成功响应头部节的空行之后立即切换到隧道模式; 该空行之后收到的数据来自 request-target 所标识的服务器. 成功响应以外的任何响应都表示隧道尚未形成, 连接仍由 HTTP 管辖.

当隧道中间方检测到任一侧已关闭其连接时, 隧道即被关闭: 中间方 MUST 尝试把来自已关闭一侧的任何未发送数据发送到另一侧, 关闭两个连接, 然后丢弃任何剩余未递送数据.

代理认证可用于建立创建隧道的权限. 例如,

CONNECT server.example.com:80 HTTP/1.1
Host: server.example.com:80
Proxy-Authorization: basic aGVsbG86d29ybGQ=

建立到任意服务器的隧道存在重大风险, 尤其当目的地是众所周知或保留的 TCP 端口且该端口并非用于 Web 流量时. 例如, 对 "example.com:25" 执行 CONNECT 会暗示代理连接到 SMTP 流量的保留端口; 如果允许, 这可能诱使代理中继垃圾邮件. 支持 CONNECT 的代理 SHOULD 将其使用限制为一组有限的已知端口, 或一个可配置的安全请求目标列表.

服务器 MUST NOT 在对 CONNECT 的 2xx (Successful) 响应中发送任何 Transfer-EncodingContent-Length 头部字段. 客户端 MUST 忽略在对 CONNECT 的成功响应中收到的任何 Content-LengthTransfer-Encoding 头部字段.

CONNECT 请求消息中的负载没有已定义语义; 在 CONNECT 请求中发送负载体可能导致某些现有实现拒绝该请求.

CONNECT 方法的响应不可缓存.

4.3.7. OPTIONS

OPTIONS 方法请求有关目标资源可用通信选项的信息, 这些选项可能位于源服务器, 也可能位于介入的中间方. 该方法允许客户端在不暗示资源动作的情况下, 确定与资源相关联的选项和/或要求, 或服务器的能力.

以星号 ("") 作为 request-target ([RFC7230] Section 5.3) 的 OPTIONS 请求适用于服务器整体, 而不是某个特定资源. 由于服务器的通信选项通常依赖于资源, "" 请求只适合作为 "ping" 或 "no-op" 类型的方法; 除了允许客户端测试服务器能力之外, 它什么也不做. 例如, 这可用于测试代理是否符合 HTTP/1.1 (或不符合).

如果 request-target 不是星号, OPTIONS 请求适用于与目标资源通信时可用的选项.

生成对 OPTIONS 的成功响应的服务器 SHOULD 发送任何可能指示服务器已实现且适用于目标资源的可选功能的头部字段 (例如 Allow), 包括本规范未定义的潜在扩展. 响应负载 (如果有) 也可以用机器可读或人类可读的表示描述通信选项. 本规范未定义此类表示的标准格式, 但未来的 HTTP 扩展可能会定义. 如果响应中不发送负载体, 服务器 MUST 生成一个值为 "0" 的 Content-Length 字段.

客户端 MAY 在 OPTIONS 请求中发送 Max-Forwards 头部字段, 以目标定位请求链中的特定接收者 (见 Section 5.1.2). 代理在转发请求时 MUST NOT 生成 Max-Forwards 头部字段, 除非它收到的请求已带有 Max-Forwards 字段.

生成包含负载体的 OPTIONS 请求的客户端 MUST 发送有效的 Content-Type 头部字段来描述表示媒体类型. 尽管本规范未定义这类负载的任何用途, 未来的 HTTP 扩展可能会使用 OPTIONS 主体来对目标资源进行更详细的查询.

OPTIONS 方法的响应不可缓存.

4.3.8. TRACE

TRACE 方法请求对请求消息执行远程的应用层回环. 请求的最终接收者 SHOULD 将收到的消息 (排除下面描述的一些字段) 作为 200 (OK) 响应的消息体反射回客户端, 并使用 Content-Type "message/http" ([RFC7230] Section 8.3.1). 最终接收者要么是源服务器, 要么是请求中第一个收到 Max-Forwards 值为零 (0) 的服务器 (Section 5.1.2).

客户端 MUST NOT 在 TRACE 请求中生成包含敏感数据且可能被响应披露的头部字段. 例如, 用户代理在 TRACE 请求中发送已存储的用户凭据 [RFC7235] 或 Cookie [RFC6265] 是不明智的. 当最终接收者生成响应主体时, SHOULD 排除任何可能包含敏感数据的请求头部字段.

TRACE 允许客户端看到请求链另一端收到的内容, 并将这些数据用于测试或诊断信息. Via 头部字段 ([RFC7230] Section 5.7.1) 的值特别有意义, 因为它充当请求链的跟踪记录. 使用 Max-Forwards 头部字段允许客户端限制请求链长度, 这对于测试一串代理是否在无限循环中转发消息很有用.

客户端 MUST NOT 在 TRACE 请求中发送消息体.

TRACE 方法的响应不可缓存.