Answers:
如果您使用适当的标头,则第9.5节(POST)中相应的RFC 2616允许将对POST消息的响应进行缓存。
除非响应包括适当的Cache-Control或Expires标头字段,否则对此方法的响应不可缓存。但是,303(请参阅其他)响应可用于指导用户代理检索可缓存资源。
请注意,相同的RFC在第13节(HTTP中的缓存)中明确规定,缓存必须在POST 请求后使相应的实体无效。
某些HTTP方法必须导致缓存使实体无效。这是Request-URI或Location或Content-Location标头(如果存在)所引用的实体。这些方法是:
- PUT - DELETE - POST
我不清楚这些规范如何实现有意义的缓存。
这在RFC 7231(第4.3.3节)中也得到反映和进一步阐明,该文件已淘汰RFC 2616。
对POST请求的响应仅在包含
显式的新鲜度信息时才可缓存(请参阅[RFC7234]的4.2.1节)。
但是,POST缓存并未广泛实施。对于原始服务器希望客户端能够以一种可以被以后的GET重用的方式缓存POST结果的情况,原始服务器可以发送一个200(OK)响应,其中包含结果和一个Content-Location。标头字段,其值与POST的有效请求URI相同(第3.1.4.2节)。
据此,可以将缓存的POST的结果(如果此功能由服务器指示)随后用于对相同URI的GET请求。
根据RFC 2616第9.5节:
“对POST方法的响应不可缓存,除非响应包括适当的Cache-Control或Expires标头字段。”
因此,是的,您可以缓存POST请求响应,但前提是它带有适当的标头。在大多数情况下,您不想缓存响应。但是在某些情况下(例如,如果您没有在服务器上保存任何数据),这是完全适当的。
请注意,无论标题如何,许多浏览器(包括当前的Firefox 3.0.10)都不会缓存POST响应。IE在这方面表现得更聪明。
现在,我想在这里消除对RFC 2616 S. 13.10的困惑。URI上的POST方法不会“使用于缓存的资源无效”,正如某些人在此处所述。即使它的缓存控制标头指示了较长的持续时间,它也会使该URI过时的先前缓存的版本。
GET
和POST
请求使用相同的URI时,“制作URI过期的缓存版本”适用。如果您是位于客户端和服务器之间的缓存,则会看到GET /foo
并缓存响应。接下来您会看到,即使响应不包含任何缓存控制标头,因为它们是相同的URI,POST /foo
也需要使来自的缓存响应无效,因此即使原始标头指示缓存仍为URI,下一个也必须重新验证。直播(如果您没有看到请求)GET /foo
POST
GET /foo
POST /foo
But in some cases - such as if you are not saving any data on the server - it's entirely appropriate.
。那么,这样的POST API有什么意义呢?
总体:
基本上,POST不是幂等操作。因此,您不能将其用于缓存。GET应该是幂等的操作,因此通常用于缓存。
请参阅HTTP 1.1 RFC 2616 S.9.1的9.1节。
除了GET方法的语义外:
POST方法本身在语义上意味着将某些内容发布到资源。无法缓存POST,因为如果您一次执行一次,两次执行,三次执行某项操作,则每次更改服务器的资源。每个请求都很重要,应该传递给服务器。
PUT方法本身在语义上意在放置或创建资源。这是一个幂等操作,但不会用于缓存,因为在此期间可能已发生DELETE。
DELETE方法本身在语义上意在删除资源。这是一个幂等的操作,但不会用于缓存,因为在此期间可能发生了PUT。
关于客户端缓存:
Web浏览器将始终转发您的请求,即使它具有先前POST操作的响应。例如,您可能隔两天发送含gmail的电子邮件。它们可能是相同的主题和正文,但应同时发送两封电子邮件。
关于代理缓存:
将您的消息转发到服务器的代理HTTP服务器将永远不会缓存除GET或HEAD请求之外的任何内容。
关于服务器缓存:
默认情况下,服务器不会通过检查其缓存来自动处理POST请求。但是当然可以将POST请求发送到您的应用程序或加载项,并且当参数相同时,您可以拥有自己的缓存来读取。
使资源无效:
检查HTTP 1.1 RFC 2616 S.13.10显示POST方法应使用于缓存的资源无效。
如果您确实缓存了POST响应,则该响应必须在Web应用程序的指导下。这就是“对此方法的响应是不可缓存的,除非响应包括适当的Cache-Control或Expires标头字段”。
可以放心地假设,知道POST结果是否幂等的应用程序决定是否附加必要和适当的缓存控制标头。如果存在建议缓存的标头,则该应用程序会告诉您POST实际上是一个超级GET。仅仅由于执行幂等操作所需的不必要和无关的数据量(使用URI作为缓存键)才需要使用POST。
在这种假设下,可以从缓存中提供以下GET。
无法附加必要且正确的标头以区分可缓存和不可缓存POST响应的应用程序因任何无效的缓存结果而出错。
也就是说,每个到达缓存的POST都需要使用条件标头进行验证。为了刷新缓存内容,这是必需的,以避免直到对象的生存期到期后,POST的结果才不会反映在对请求的响应中。
Mark Nottingham分析了何时可以缓存POST响应。请注意,要利用缓存的后续请求必须是GET或HEAD请求。另请参见http语义
POST不处理已标识状态的表示形式(占100个状态的99倍)。当服务器通过设置与请求URI相同的Content-Location标头来表示此POST响应是其URI的表示形式时。发生这种情况时,POST响应就像是对相同URI的GET响应;可以缓存和重用它-但仅用于将来的GET请求。
如果它实际上并没有改变您网站上的数据,则应该是GET请求。即使是表单,您仍然可以将其设置为get请求。就像其他人指出的那样,虽然您可以缓存POST的结果,但是这在语义上是没有意义的,因为POST从定义上说就是在更改数据。
如果您想知道是否可以缓存发帖请求,并尝试研究该问题的答案,那么您可能不会成功。当搜索“缓存发布请求”时,第一个结果是这个StackOverflow问题。
答案混淆了缓存的工作方式,如何根据RFC进行缓存,如何根据RFC进行缓存以及实际上如何进行缓存。让我们从RFC开始,逐步演示浏览器的实际工作方式,然后讨论CDN,GraphQL和其他相关领域。
根据RFC,POST请求必须使缓存无效:
13.10 Invalidation After Updates or Deletions
..
Some HTTP methods MUST cause a cache to invalidate an entity. This is
either the entity referred to by the Request-URI, or by the Location
or Content-Location headers (if present). These methods are:
- PUT
- DELETE
- POST
这种语言表明POST请求不可缓存,但这不是正确的(在这种情况下)。缓存仅对先前存储的数据无效。RFC(似乎)明确说明是的,您可以缓存POST
请求:
9.5 POST
..
Responses to this method are not cacheable, unless the response
includes appropriate Cache-Control or Expires header fields. However,
the 303 (See Other) response can be used to direct the user agent to
retrieve a cacheable resource.
尽管使用了这种语言,设置Cache-Control
不得将后续POST
请求缓存到同一资源。POST
请求必须发送到服务器:
13.11 Write-Through Mandatory
..
All methods that might be expected to cause modifications to the
origin server's resources MUST be written through to the origin
server. This currently includes all methods except for GET and HEAD.
A cache MUST NOT reply to such a request from a client before having
transmitted the request to the inbound server, and having received a
corresponding response from the inbound server. This does not prevent
a proxy cache from sending a 100 (Continue) response before the
inbound server has sent its final reply.
这有什么意义?好吧,您不是在缓存POST
请求,而是在缓存资源。
POST响应正文只能为后续对同一资源的GET请求进行缓存。在POST响应中设置Location
或Content-Location
标头,以传达正文代表的资源。因此,缓存POST请求的唯一技术上有效的方法是对相同资源进行后续GET。
正确的答案是:
尽管RFC允许将请求缓存到同一资源,但实际上,浏览器和CDN不会实现此行为,并且不允许您缓存POST请求。
资料来源:
给定以下示例JavaScript应用程序(index.js):
const express = require('express')
const app = express()
let count = 0
app
.get('/asdf', (req, res) => {
count++
const msg = `count is ${count}`
console.log(msg)
res
.set('Access-Control-Allow-Origin', '*')
.set('Cache-Control', 'public, max-age=30')
.send(msg)
})
.post('/asdf', (req, res) => {
count++
const msg = `count is ${count}`
console.log(msg)
res
.set('Access-Control-Allow-Origin', '*')
.set('Cache-Control', 'public, max-age=30')
.set('Content-Location', 'http://localhost:3000/asdf')
.set('Location', 'http://localhost:3000/asdf')
.status(201)
.send(msg)
})
.set('etag', false)
.disable('x-powered-by')
.listen(3000, () => {
console.log('Example app listening on port 3000!')
})
并给出以下示例网页(index.html):
<!DOCTYPE html>
<html>
<head>
<script>
async function getRequest() {
const response = await fetch('http://localhost:3000/asdf')
const text = await response.text()
alert(text)
}
async function postRequest(message) {
const response = await fetch(
'http://localhost:3000/asdf',
{
method: 'post',
body: { message },
}
)
const text = await response.text()
alert(text)
}
</script>
</head>
<body>
<button onclick="getRequest()">Trigger GET request</button>
<br />
<button onclick="postRequest('trigger1')">Trigger POST request (body 1)</button>
<br />
<button onclick="postRequest('trigger2')">Trigger POST request (body 2)</button>
</body>
</html>
安装NodeJS,Express,然后启动JavaScript应用程序。在浏览器中打开网页。尝试几种不同的方案来测试浏览器的行为:
这表明,即使可以设置Cache-Control
和Content-Location
响应头,也无法使浏览器缓存HTTP POST请求。
浏览器的行为不是可配置的,但是如果您不是浏览器,则不一定受RFC规则的约束。
如果您正在编写应用程序代码,则不会阻止您显式缓存POST请求(伪代码):
if (cache.get('hello')) {
return cache.get('hello')
} else {
response = post(url = 'http://somewebsite/hello', request_body = 'world')
cache.put('hello', response.body)
return response.body
}
CDN,代理和网关也不一定必须遵循RFC。例如,如果您将Fastly用作CDN,则Fastly允许您编写自定义VCL逻辑以缓存POST请求。
您的POST请求是否应该缓存取决于上下文。
例如,您可以在基础查询为幂等的情况下使用POST查询Elasticsearch或GraphQL。在那些情况下,根据用例来缓存响应可能有意义,也可能没有意义。
在RESTful API中,POST请求通常会创建资源,因此不应进行缓存。这也是RFC对POST的理解,即它不是幂等操作。
如果您使用的是GraphQL,并且需要跨CDN和浏览器进行HTTP缓存,请考虑使用GET方法而不是POST来满足您的要求。请注意,不同的浏览器和CDN可能具有不同的URI长度限制,但作为面向外部生产GraphQL应用程序的最佳实践,操作安全列表(查询白名单)可以缩短URI。
使用firefox 27.0和httpfox,在2014年5月19日,我看到了这一行:00:03:58.777 0.488 657(393)POST(缓存)文本/ html https://users.jackiszhp.info/S4UP
显然,post方法的响应已缓存,并且也位于https中。难以置信的!
POST用于有状态的Ajax。返回POST的缓存响应会破坏通信通道和接收消息的副作用。这是非常非常糟糕。追踪也是一个真正的痛苦。强烈建议反对。
一个简单的例子是一条消息,作为副作用,它在当前周支付您的薪水10,000美元。您不想得到“确定,它成功了!” 上周缓存的页面返回。在现实世界中,其他更复杂的案例也会导致类似的欢闹。