【问题标题】:How to know when to send a 304 Not Modified response如何知道何时发送 304 Not Modified 响应
【发布时间】:2010-09-05 11:49:48
【问题描述】:

我正在编写一种资源处理方法,用于控制对各种文件的访问,并且我希望能够利用浏览器的缓存。我的问题有两个:

  1. 为了确定是否应该发送 304 响应,我需要检查哪些明确的 HTTP 标头,以及在检查它们时我在寻找什么?

  2. 此外,当我最初将文件(如“Last-Modified”)作为 200 响应发送时,是否需要发送任何标题?

一些伪代码可能是最有用的答案。


缓存控制头呢?它的各种可能值是否会影响您发送给客户端的内容(即 max-age),还是应该只遵守 if-modified-since?

【问题讨论】:

  • 我想补充一下,在发送 304 响应时,您应该只发送标头而不发送内容。

标签: language-agnostic http


【解决方案1】:

这是我实现它的方式。该代码已经运行了一年多,并且适用于多个浏览器,所以我认为它非常可靠。这是基于RFC 2616 并通过观察各种浏览器发送的内容和时间。

这是伪代码:

server_etag = gen_etag_for_this_file(myfile) etag_from_browser = get_header("Etag") 如果 etag_from_browser 不存在: etag_from_browser = get_header("If-None-Match") 如果浏览器引用了 etag: 去掉引号(例如“foo”-> foo) 将 server_etag 设置为 http 标头 如果 etag_from_browser 匹配 server_etag 向浏览器发送304返回码

这是我处理此问题的服务器逻辑的 sn-p。

/* 客户端应该设置 Etag 或 If-None-Match */ /* 一些客户引用参数,如果是这样,去掉引号 */ mketag(etag, &sb); etagin = apr_table_get(r->headers_in, "Etag"); 如果(etagin == NULL) etagin = apr_table_get(r->headers_in, "如果-无匹配"); if (etag != NULL && etag[0] == '"') { 诠释 sl; sl = strlen(etag); memmove(etag,etag+1,sl+1); etag[sl-2] = 0; logit(2,"etag=:%s:",etag); } ... apr_table_add(r->headers_out, "ETag", etag); ... if (etagin != NULL && strcmp(etagin, etag) == 0) { /* 如果 etag 匹配,我们返回 304 */ rc = HTTP_NOT_MODIFIED; }

如果您在 etag 生成方面需要一些帮助,请发布另一个问题,我也会挖掘出一些代码。 HTH!

【讨论】:

  • 您真的遇到过在请求中发送ETag 标头的客户端吗?它只应该用于响应。此外,它应始终根据规范进行引用。
  • Matt,客户端发回它之前收到的 etag,因此服务器可以决定 HTTP_NOT_MODIFIED 是否是适当的响应。我当时的主要工作是使用 Firefox 和 Safari。如果先前对给定资源的请求已将 etag 提供给客户端,则它们都将包含存储的 etag。在此处查看“典型用法”:en.wikipedia.org/wiki/HTTP_ETag
  • 客户端将其 etag 包含在 If-None-Match/If-Match 标头中。我从未见过 UA 在请求中发送 ETag: "..." 标头,是吗?
  • 嗯,我很难记住大约 4-5 年前编写的确切代码。根据我上面的伪代码,看起来我正在检查 ETag 和 If-None-Match。也许ETag检查没用?
【解决方案2】:

带有 If-Modified-Since ("IMS") 或 If-Not-Match ("INM") 标头的 GET 或 HEAD 请求可能会导致 304 Not Modified 响应。

为了决定在收到这些标头时要做什么,假设您正在处理没有这些条件标头的 GET 请求。确定响应中的 ETag 和 Last-Modified 标头的值,并使用它们来做出决定。希望您已经构建了自己的系统,使得确定这一点比构建完整响应的成本更低。

如果存在 INM,并且该标头的值与您在 ETag 中放置的值相同,则以 304 响应。

如果存在 IMS,并且该标头中的日期值晚于您在 Last-Modified 中放置的日期值,则使用 304 响应。

否则,就好像请求不包含这些标头一样继续。

对于问题的第 2 部分,要以最省力的方式解决问题,请找出您可以在 Web 应用程序中轻松正确地生成哪些(Expires、ETag 和 Last-Modified)标头。

推荐阅读材料:

http://www.w3.org/Protocols/rfc2616/rfc2616.html

http://www.mnot.net/cache_docs/

【讨论】:

    【解决方案3】:

    如果客户端明确声明它可能已经在其缓存中拥有该页面,您应该发送 304。这称为条件 GET,它应在请求中包含 if-modified-since 标头。

    基本上,此请求标头包含客户端声称拥有缓存副本的日期。您应该检查在此日期之后内容是否发生了变化,如果没有,则发送 304。

    有关 RFC 中的相关部分,请参阅 http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25

    【讨论】:

      【解决方案4】:

      我们还处理缓存但安全的资源。如果您发送/生成一个 ETAg 标头(RFC 2616 第 13.3 节建议您应该),那么客户端必须在条件请求中使用它(通常在 If-None-Match - HTTP_IF_NONE_MATCH - 标头中)。如果您发送 Last-Modified 标头(同样您应该),那么您应该检查 If-Modified-Since - HTTP_IF_MODIFIED_SINCE - 标头。如果你发送两个,那么客户端应该发送两个,但它必须发送 ETag。另请注意,验证只是定义为检查条件标头是否与您要发送的标头严格相等。此外,只有强验证器(例如 ETag)才会用于范围请求(仅请求资源的一部分)。

      实际上,由于我们保护的资源是相当静态的,一秒钟的延迟时间是可以接受的,我们正在执行以下操作:

      1.  检查用户是否有权访问请求的资源

             如果不是,请重定向它们或根据需要发送 4xx 响应。我们将对看起来像黑客攻击或公然尝试执行安全终止运行的请求生成 404 响应。

      2.  将 If-Modified-Since 标头与我们发送的 Last-Modified 标头进行比较(见下文)以实现严格相等

           如果匹配,则发送 304 Not Modified 响应并退出页面处理

      3.  使用请求资源的修改时间创建 Last-Modified 标头

           在 RFC 2616 中查找 HTTP 日期格式

      4.  发送标头和资源内容以及适当的 Content-Type

      我们决定避开 ETag 标头,因为它对我们的目的来说太过分了。我想我们也可以只使用日期时间戳作为 ETag。如果我们转向真正的 ETag 系统,我们可能会存储资源的计算哈希值并将其用作 ETags。

      如果您的资源是从数据库内容动态生成的,那么 ETag 可能更适合您的需求,因为它们只是您认为合适的文本。

      【讨论】:

        【解决方案5】:

        关于缓存控制:

        在提供服务时,您不必担心缓存控制,只需将其设置为合理的值。它基本上是告诉浏览器和其他下游实体(例如代理)在缓存超时之前应该经过的最长时间。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-03-29
          • 2016-08-08
          • 1970-01-01
          • 1970-01-01
          • 2014-01-25
          • 2014-07-22
          • 2012-08-28
          • 2021-08-16
          相关资源
          最近更新 更多