如何判断何时发送304未修改的响应

12

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

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

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

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


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


1
我想补充一点,当发送304响应时,您只需发送标头而不是内容。 - GateKiller
5个回答

8
这是我如何实现它的。代码已经运行了一年多,使用了多个浏览器,所以我认为它非常可靠。这是基于RFC 2616和观察各种浏览器发送什么和何时发送的。
以下是伪代码:
server_etag = 生成此文件的ETag(myfile) etag_from_browser = 获取头部("Etag")
如果浏览器没有提供ETag: etag_from_browser = 获取头部("If-None-Match") 如果浏览器引用了ETag: 去掉引号(例如 "foo" --> foo)
将server_etag设置到HTTP头中
如果etag_from_browser与server_etag匹配 发送304返回码给浏览器
这是处理它的服务器逻辑片段。
/* 客户端应该设置ETag或If-None-Match */ /* 一些客户端引用了参数,如果是这样就去掉引号 */ mketag(etag, &sb);
etagin = apr_table_get(r->headers_in, "Etag"); if (etagin == NULL) etagin = apr_table_get(r->headers_in, "If-None-Match"); if (etag != NULL && etag[0] == '"') { int 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生成的帮助,请发另一个问题,我会找出一些代码并提供帮助。希望对您有所帮助!


你是否真的遇到过在请求中发送 ETag 标头的客户端?它只应该在响应中使用。此外,根据规范,它应该始终被引用。 - Matt Kantor
Matt,客户端会将之前收到的etag发送回服务器,以便服务器可以决定是否适当地返回HTTP_NOT_MODIFIED响应。我在这个时候主要使用Firefox和Safari进行工作;如果之前对给定资源的请求已经向客户端提供了一个etag,它们都会包含存储的etag。请在此处查看“典型用法”:http://en.wikipedia.org/wiki/HTTP_ETag - Mark Harrison
客户端在 If-None-Match/If-Match 头中包含它的 etag(s)。我从未见过 UA 在请求中发送 ETag: "..." 头,你见过吗? - Matt Kantor
嗯,我有点难以记起大约4-5年前写的确切代码。根据我上面的伪代码,看起来我当时正在检查ETag和If-None-Match两者。也许ETag检查是无用的? - Mark Harrison

4

一个304未修改的响应可以由带有If-Modified-Since(“IMS”)或If-Not-Match(“INM”)头的GET或HEAD请求导致。

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

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

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

否则,请按照不包含这些头的请求进行处理。

对于第二部分问题的最小工作量方法,请确定您的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状态码。请参见http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25相关RFC部分。

2
我们还处理缓存,但是保证了安全的资源。如果您发送/生成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系统,我们可能会存储资源的计算哈希,并将其用作ETag。
如果您的资源是动态生成的,例如从数据库内容生成,则ETag可能更适合您的需求,因为它们只是要根据需要填充的文本。

1

关于缓存控制:

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接