14

RFC 2616Location标头定义为:

Location response-header 字段用于将接收者重定向到 Request-URI 以外的位置,以完成请求或识别新资源
……
对于 3xx 响应,该位置应指示服务器的首选 URI 用于自动重定向到资源。

AFAIK,对于3xx Redirection代码,Location标题是:

  • 300多项选择:可选
  • 301 永久移动:必填
  • 302 找到:必填
  • 303 见其他:必填
  • 304未修改:不相关
  • 305使用代理:不相关(?)
  • 306 切换代理:不相关 (?)
  • 307 临时重定向:必需
  • 308 永久重定向:必需

但这只是来自个人经验。是否有定义哪些 HTTP 代码需要发送标Location头的标准?

也就是说,对于哪些 3xx 代码,HTTP 客户端在收到没有相应Location标头的情况下应该抛出异常?

4

1 回答 1

18

在 RFC 2616 仍然是权威的日子里,这个问题已经被问到了,所以现在 RFC 7230 到 7235 已经到位,这看起来像是一个有趣的研究项目。那么,让我们看看我们在这里得到了什么。

Location头现在在RFC 7231 第 7.1.2 节中定义:

在某些响应中使用“Location”标头字段来引用与响应相关的特定资源。关系类型由请求方法和状态码语义组合定义。

[…]

对于201(已创建)响应,位置值是指请求创建的主要资源。对于 3xx(重定向)响应,Location 值是指用于自动重定向请求的首选目标资源。

该部分不将此标头仅限制为状态代码的 3xx 范围。事实上,唯一明确提到的状态代码是 201 (Created) 和303 (See Other)。但是,没有任何状态代码实际需要此标头的消息。

RFC 7231 第 6.4 节现在描述了 3xx 范围代码的用途:

3xx(重定向)类状态码表示用户代理需要采取进一步的行动来满足请求。 如果提供了 Location 头字段,用户代理可以自动将其请求重定向到 Location 字段值所引用的 URI,即使不理解特定的状态代码。

该措辞表明,存在或自动重定向到其内容都不是强制性的。

在撰写本文时,IANA HTTP 状态代码注册表将代码 300 到 308 列为已注册。一个(305)被废弃,一个被保留(306),剩下七个活动代码:

300:多项选择 - RFC 7231,第 6.4.1 节

如果服务器知道资源的多个表示,则返回 300 代码。从 RFC 7231 开始,不再推荐Link通过RFC 5988的标头来传达可能的表示列表的方法。关于Location标头,RFC 有这样的说法:

如果服务器有首选,服务器应该生成一个包含首选 URI 引用的 Location 头字段。用户代理可以使用 Location 字段值进行自动重定向。

这意味着Location仅当服务器具有首选表示时才使用标头。如果没有,服务器根本没有这样的偏好。

值得一提的是,Location标题本身不适合列出所有可能的表示,因为它的语法是一个不能包含列表的单值字段。因此,

Location: //example.com/a
Location: //example.com/b

未定义。

301:永久移动 – RFC 7231,第 6.4.2 节

此响应代码是为了让客户端知道所请求的资源有一个全新的位置:后续请求将被定向到Location标头中指定的位置。

服务器应该在响应中生成一个 Location 头字段,其中包含新永久 URI 的首选 URI 引用。用户代理可以使用 Location 字段值进行自动重定向。

同样,Location标题的存在不是绝对要求。缺少此标题的实用性值得怀疑。语义类似于 - 但不等于 - 410 (Gone)响应:“此资源已永久移动到一个新的但未知的位置。”

302:找到 – RFC 7231,第 6.4.3 节

最初这已被指定为“临时重定向”并在以后的规范中重命名。与 301 相比,这个不能(或不应该)被缓存或用于永久重写 URL。规范的相关部分内容如下:

服务器应该在包含不同 URI 的 URI 引用的响应中生成 Location 头字段。用户代理可以使用 Location 字段值进行自动重定向。

我相信缺少Location标头的语义与 301 几乎相同:“此资源已暂时移动到一个新的但未知的位置。”

303:参见其他 – RFC 7231,第 6.4.4 节

303 应该响应POST请求而返回,但适用于任何方法。一般来说,它的目的是让客户端知道在替代 URL 处有更合适的表示,或者请求的资源无法通过 HTTP 传输。

在这个问题的背景下,这有点让人头疼。RFC 2616,第 10.3.4 节指出:

不同的 URI 应该由响应中的 Location 字段给出。

较新的 RFC 7231 的相关部分似乎只是假设Location存在标头:

服务器将用户代理重定向到不同的资源,如 Location 标头字段中的 URI 所示

勘误表中没有任何内容可以澄清这一点,因此我倾向于假设 RFC 2616 的位置。缺少Location标头的语义确实因请求方法而异:

304:未修改 – RFC 7232,第 4.1 节

这个响应在某种程度上是特别的,因为它强调“[indication] 用户代理需要采取进一步的行动来满足请求。” 它应该被理解为不是重定向到新的 URI 而是重定向到本地缓存LocationRFC 7232 的相关部分根本没有提到标头。事实上,这对于我的理解来说意义不大,语义类似于“请求的该实体的表示仍然没有链接,你会在本地缓存中找到它......”这是对关注点分离的极大破坏,但是并不是说Location在这个地方不允许。仍然,Content-Location或者Link带有rel=self部分的标题更合适。前一个正在接受明确的提及:

生成 304 响应的服务器必须生成以下任何一个头字段,这些字段将在对同一请求的200(OK)响应中发送:Cache-ControlContent-LocationDateETagExpiresVary

305:使用代理 - RFC 2616,第 10.3.6 节RFC 7231,第 6.4.5 节

出于安全考虑,从 RFC 7231 开始不推荐使用此状态代码(参见附录 B)。它在 RFC 2616 中的定义如下:

请求的资源必须通过 Location 字段给出的代理访问。

意味着存在Location标头,但它没有明确要求它。省略此标头将具有“此资源只能通过某些代理访问”的语义含义。

306:切换代理 - draft-cohen-http-305-306-responses-00

该代码是在 RFC 2068 最终确定并已被 RFC 2616 淘汰后作为草案引入的。据我所知,该草案从未达到推荐的状态,所以这纯粹是为了完整性。该草案的基本原理是为代理提供一种机制,以将客户(临时)引导到其他代理以进行后续请求。

该草案的一部分是引入了Set-Proxy标题,该标题将用于代替第 2.2 节Location中的标题:

在最初的 HTTP/1.1 规范中,“Location”标头用于指示代理设置。在 305 响应的上下文中,“Set-proxy”标头已弃用它的使用。所有新的实现必须发送 Set-proxy 标头。实现可以发送 'Location' 标头以允许向后兼容。

Set-Proxy然后在 306 的上下文中是必需Location的,而标题纯粹是可选的。由于所需的Set-Proxy机制旨在替换Location,因此缺少后一个标头不会引入语义更改。

307:临时重定向 – RFC 7231,第 6.4.7 节

307 是由于 HTTP/1.1 中 302 的语义更改而引入的:虽然通过 302 重定向可以更改请求方法,但重定向的请求必须具有与原始请求相同的请求方法。

规范的相关部分内容如下:

服务器应该在包含不同 URI 的 URI 引用的响应中生成 Location 头字段。用户代理可以使用 Location 字段值进行自动重定向。

同样,Location似乎是可选的。对于由于缺少标头而导致的语义变化,请参见 302。

308:永久重定向 – RFC 7538

与 307 一样,通过 308 进行的重定向将保留其原始请求方法。可以说 308 对 301 就像 307 对 302 一样。

从规范的第 3 节:

服务器应该在响应中生成一个 Location 头字段,其中包含新永久 URI 的首选 URI 引用。

所以,总而言之,我们遇到了这种情况:

  • 隐含:1 (305)
  • 可选:1 (306)
  • 未提及:1 (304)
  • 应该:6(300;301;302;303;307;308)

“应该”将在RFC 2119的上下文中阅读:

这个词或形容词“推荐”意味着在特定情况下可能存在忽略特定项目的正当理由,但在选择不同的课程之前必须理解并仔细权衡全部含义。

这与“必须”或“需要”(也在该 RFC 中)的绝对要求不同。简而言之:没有 3xx 类代码中的Location标头是强制性的。

需要注意的是,缺少Location标头的问题并不是一个新问题。从另一个答案

301、302、303 和 307 仅在下一个 URL 已知时才提供位置。否则,客户/用户必须决定下一步做什么

于 2016-05-08T15:04:21.227 回答