如何知道何时发送 304 Not Modified 响应
-
08-06-2019 - |
题
我正在编写一个资源处理方法,在其中控制对各种文件的访问,并且我希望能够利用浏览器的缓存。我的问题有两个:
我需要检查哪些明确的 HTTP 标头,以便确定是否应该发送 304 响应,以及检查它们时我要查找什么?
此外,当我最初发送文件(例如“Last-Modified”)作为 200 响应时,是否需要发送任何标头?
一些伪代码可能是最有用的答案。
缓存控制头怎么样?它的各种可能值是否会影响您发送给客户端的内容(即 max-age),还是只应遵守 if-modified-since?
解决方案
我是这样实现的。该代码已经运行了一年多,并且适用于多种浏览器,因此我认为它非常可靠。这是基于 RFC 2616 并通过观察各种浏览器发送的内容和时间。
这是伪代码:
server_etag = gen_etag_for_this_file(myfile) etag_from_browser = get_header("Etag") if etag_from_browser does not exist: etag_from_browser = get_header("If-None-Match") if the browser has quoted the etag: strip the quotes (e.g. "foo" --> foo) set server_etag into http header if etag_from_browser matches server_etag send 304 return code to browser
这是我处理此问题的服务器逻辑的片段。
/* the client should set either Etag or If-None-Match */ /* some clients quote the parm, strip quotes if so */ 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) { /* if the etag matches, we return a 304 */ rc = HTTP_NOT_MODIFIED; }
如果您需要有关 etag 生成的帮助,请发布另一个问题,我将找出一些也可以实现此目的的代码。哈!
其他提示
304 Not Modified 响应可以由带有 If-Modified-Since (“IMS”) 或 If-Not-Match (“INM”) 标头的 GET 或 HEAD 请求产生。
为了决定在收到这些标头时做什么,请假设您正在处理没有这些条件标头的 GET 请求。确定该响应中 ETag 和 Last-Modified 标头的值,并使用它们做出决定。希望您已经构建了系统,以便确定这一点比构建完整响应的成本更低。
如果存在 INM 并且该标头的值与您在 ETag 中放置的值相同,则使用 304 进行响应。
如果存在 IMS 并且该标头中的日期值晚于您在 Last-Modified 中放置的日期值,则使用 304 进行响应。
否则,继续处理,就好像请求不包含这些标头一样。
为了以最省力的方法解决问题的第 2 部分,请找出您可以在 Web 应用程序中轻松正确地生成哪些(Expires、ETag 和 Last-Modified)标头。
对于建议的阅读材料:
如果客户端明确声明其缓存中可能已存在该页面,则您应该发送 304。这称为条件 GET,其中应包括 如果-修改-自 请求中的标头。
基本上,此请求标头包含客户端声称拥有缓存副本的日期。您应该检查内容在此日期之后是否发生更改,如果没有更改,则发送 304。
看 http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25 RFC 中的相关部分。
我们还处理缓存但安全的资源。如果您发送/生成 ETAg 标头(RFC 2616 第 13.3 节建议您应该这样做),则客户端必须在条件请求中使用它(通常在 If-None-Match - HTTP_IF_NONE_MATCH - 标头中)。如果您发送 Last-Modified 标头(您再次应该),那么您应该检查 If-Modified-Since - HTTP_IF_MODIFIED_SINCE - 标头。如果两者都发送,那么客户端应该发送两者,但必须发送 ETag。另请注意,验证仅定义为检查条件标头是否与您要发送的标头严格相等。此外,只有强验证器(例如 ETag)才会用于范围请求(仅请求部分资源)。
在实践中,由于我们保护的资源相当静态,并且一秒的延迟时间是可以接受的,因此我们正在执行以下操作:
检查用户是否有权访问所请求的资源
如果不是,请重定向它们或根据需要发送 4xx 响应。我们将对看起来像是黑客尝试或公然尝试执行安全最终运行的请求生成 404 响应。
将 If-Modified-Since 标头与我们要发送的 Last-Modified 标头进行比较(见下文)以确保严格相等
如果匹配,则发送 304 Not Modified 响应并退出页面处理
使用请求资源的修改时间创建 Last-Modified 标头
在 RFC 2616 中查找 HTTP 日期格式
发送标头和资源内容以及适当的 Content-Type
我们决定避开 ETag 标头,因为它对于我们的目的来说太过分了。我想我们也可以使用日期时间戳作为 ETag。如果我们转向真正的 ETag 系统,我们可能会存储资源的计算哈希值并将其用作 ETag。
如果您的资源是从数据库内容动态生成的,那么 ETag 可能更适合您的需求,因为它们只是根据您认为合适的方式填充的文本。
关于缓存控制:
除了将其设置为合理的值之外,您在提供服务时不必担心缓存控制。它基本上告诉浏览器和其他下游实体(例如代理)缓存超时之前应该经过的最长时间。