繁体   English   中英

带有“Cache-Control: no-cache”的 HTTP `GET` 请求是否可以不准确地访问服务器一次? (利用 `GET` 的幂等性。)

[英]Is it possible for an HTTP `GET` request with `Cache-Control: no-cache` to not hit the server exactly once? (Levering out idempotency of `GET`.)

理论上,应该只对幂等请求使用 HTTP GET方法。

但是,由于一些复杂的原因,我不能使用GET以外的任何其他方法,而且我的请求不是幂等的(它们会改变数据库)。 所以我的想法是使用Cache-Control: no-cache header 来确保任何GET请求实际命中数据库。 此外,我无法更改 URL,这意味着我无法将随机 URL 参数 append 用于破坏缓存。

我是安全的还是应该实施某种机制来确保只收到一次GET请求? (客户端是浏览器,服务器是Node.js。)

如果GET请求被某个中间人复制,导致服务器两次接收到相同的GET请求,那该怎么办? 我相信规范允许这种情况,但这种情况在“现实生活”中是否发生过?

我从未见过像 Cloudflare 或 NGNIX 这样的中间人使用Cache-Control: no-cache阻止或复制GET请求。

让我们从您已经指出的内容开始——GET 请求应该是幂等的。 也就是说,它们不应该修改资源,因此每次都应该返回相同的东西(除非同时使用任何其他方法来修改它。)

值得指出的是,正如restcookbook.com 所指出的,这并不意味着请求不会改变任何内容。 相反,资源的表示不应该改变。 例如,您的数据库可能会记录请求,但不应在响应中返回不同的值。

您列出的主要问题是中间件缓存。

危险不在于中间件多次向您的服务器发送请求(您提到“复制”请求),而是(a)它发送旧的、缓存的、不再准确的响应给正在制作的任何东西请求,并且 (b) 请求没有到达服务器。

例如,假设一个响应返回一个count属性,该属性从 0 开始,并在命中 GET 端点时递增。 请求 #1 将返回“1”作为计数。 请求 #2 现在应该返回“2”作为计数,但如果它被缓存,它可能仍显示为 1,并且不会命中服务器以将计数增加到 2。这是您遇到的 2 个单独的问题(缓存,而不是更新) .

那么,中间件阻止请求到达服务器并提供缓存副本吗? 我们不知道。 这取决于中间件。 你现在绝对可以写一个做到这一点。 你也可以写一个没有的。

如果您不知道什么会消耗您的 API,那么这不是一个很好的选择。 但它是否“安全”取决于具体情况。

如您所知,最好遵循 HTTP 请求的语法附带的一组期望。 偏离它们会让你在很多方面失败。 (例如,基于方法的请求有不同的安全期望。从 CORS 的角度来看,浏览器可能将 GET 请求视为“简单”,而它永远不会这样对待 PATCH 请求。)

我会竭尽全力不打破这个约定,但如果我被迫打破这个期望,我肯定会在我的 API 文档中注明。

确保只调用一次 GET 请求的一种解决方法是允许缓存响应并使用Vary header。 Vary header 的规格可在此处找到

In summary, a Vary header basically tells any HTTP cache, which parts of the request header to take into account when trying to find the cached object.

例如,您有一个端点/api/v1/something接受 GET 请求并执行所需的数据库更新。 假设成功时,此端点返回以下响应。

HTTP/1.1 200 OK
Content-Length: 3458
Cache-Control: max-age=86400
Vary: X-Unique-ID

注意 Vary header 的值为X-Unique-ID 这意味着如果您在请求中包含X-Unique-ID header,则任何 HTTP 缓存层(无论是浏览器、CDN 还是其他中间件)都将使用此 Z099FB995346F331C749F6E40 中的值来确定是否使用先前缓存的响应或不是。

假设您发出第一个请求,其中包含一个X-Unique-ID header ,其值为id_1 ,然后您发出一个X-Unique-ID值为id_2的后续请求。 缓存层不会对第二个请求使用先前缓存的响应,因为X-Unique-ID的值不同。

但是,如果您再次发出另一个包含id_1X-Unique-ID值的请求,缓存层将不会向后端发出请求,而是假设缓存尚未过期,而是将缓存的响应重用于第一个请求.

不过,您必须考虑的一件事是,这只有在缓存层实际上遵守 Vary header 的规范时才有效。

超文本传输协议 (HTTP) 旨在实现客户端和服务器之间的通信。 其中 Get 方法用于从指定资源请求数据。 当我们使用 'Cache-control: no-cache' 时,这意味着缓存不能存储有关客户端请求或服务器响应的任何内容。 该请求到达服务器,并且每次都下载完整的响应。

这在很大程度上取决于中间的内容以及重试逻辑的位置(如果有的话)。 几乎所有的问题都在故障处理和重试处理中——而不是基本请求。

例如,假设 Alice 通过代理与 Bob 交谈。 为简单起见,我们假设请求很小,代理逻辑是纯存储转发的。 即大多数情况下,请求要么通过要么不通过,但不太可能在中途停止。 不能保证会出现这种情况,并且某些代理会在设计的过程中中途停止请求。

Alice -> Proxy GET
Proxy -> Bob GET
Bob -> Proxy 200
Proxy -> Alice 200

到目前为止,一切都很好。 现在想象 Bob 在响应代理之前死了。 代理是否重试? 如果是这样,我们有这个:

Alice -> Proxy GET
Proxy -> Bob GET
Bob manipulates database then dies
Proxy -> Bob GET (retry)
Now we have a dupe

不太可能,但可能。

现在想象(更有可能)代理(或者甚至更有可能,代理和客户端之间的一些网络)死了。 客户端是否重试? 如果是这样,我们有这个:

Alice -> Proxy GET
Proxy -> Bob GET
Bob -> Proxy 200
Proxy or network dies
Alice -> Proxy GET (retry)
Proxy -> Bob GET
Is this a dupe or not? Depends on your point of view

另外,为了完整起见,还有服务器接收请求零次的退化情况。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM