FrankKai.github.io
FrankKai.github.io copied to clipboard
HTTP缓存之ETag
- 初识ETag
- ETag是req header还是res header?
- ETag的作用是什么?(标记资源version)
- 为什么要用对资源标记版本?
- ETag还解决了什么问题?
- 如何生成一个ETag?
- ETag和什么已知的知识点类似?
- ETag可以做持久化吗?
- ETag是响应头还是请求头?
- ETag语法
- ETag指令
- 避免mid-air相撞
- 缓存未更改的资源(304 Not Modified是怎么回事?)
- 项目中的ETag分析(强验证,弱验证)
初识ETag
ETag是req header还是res header?
ETag是HTTP response header。
ETag的作用是什么?
标记资源version。 资源版本的唯一标识。 可以理解成一个唯一的hash。类似唯一标识或者版本标识。 类似uuid,primary key,package.json version,git commit hash,git tag等等。 目前接触过的唯一标识有uuid,数据库primary key等等。 目前接触过的版本号有package.json的version,commit hash等等。 目前接触过的tag有git的tag。
为什么要用对资源标记版本?
对资源标记版本的话,可以使得缓存的使用更加高效而且节省带宽。 因为如果content没有发生变化的话,server是没有必要重新发送一个完整的response到client的。
ETag还解决了什么问题?
ETag可以阻止同时更新一个资源出现的覆盖问题。这个问题叫做“mid-air collisions”。
如何生成一个ETag?
如果资源的URL发生了变化,必须生成一个新的ETag。 对比新旧资源的ETag可以判断出两个资源是不是相同的。
ETag和什么已知的知识点类似?
类似uuid,primary key,package.json version,git commit hash,git tag等等。 说得通俗易懂一些的话,与指纹类似,一些服务器可以通过ETag做到追踪。
ETag可以做持久化吗?
可以。 ETag可以设置一个持久化的值,从而服务器可以永久追踪到,也算是一种数据持久化。
ETag是响应头还是请求头?
响应头。 ETag不是一个forbidden header name。
ETag语法
ETag: W/"<etag_value>"
ETag: "<etag_value>"
ETag指令
W/ (Optional)
'W/'(大小写敏感) 的意思是weak validator(弱验证)。 弱etag是很容易生成的,但是对于比较来说就不是很有用了。 强验证是最理想化的比较情况,但是很难高效率的生成。 同一资源的两种表现形式的弱ETag的值在语义上可能是等价的,但是并不是byte级相等。 weak etag在byte range requests使用时,阻止缓存;但是strong etag意味着range requests可以缓存。
"<etag_value>" (核心)
- 实体的tag唯一代表请求的资源。
- etag由双引号扩起来的ASCII字符组成,例如:”675af34563dc-tr34“,"5615195CC81CB676D5EB5224558EB0EC"。
- etag生成的方法是不能直接指定的。
- etag通常由
content hash,last modification timestamp hash
或者直接一个版本号组成。例如mdn的etag由一个代表wiki文章内容的十六进制hash组成:etag: "5e7468cd-7de"
。
避免mid-air相撞(ETag和If-Match)
ETag与If-Match头一起作用时,可以检测到mid-air(空中)编辑冲突。 例如,在编辑MDN时,当前的wiki content被哈希化,之后在response的ETag中传入:
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
当向wiki page存储内容时(比如发送数据),POST请求将在If-Match头中包含上ETag的值,去检查这个内容是不是fresh的:
If-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
如果hash没有匹配到,意味着文档在中间被编辑,然后抛出一个412状态码代表条件失败错误。
缓存未更改的资源
ETag头的另一个典型用处就是:缓存没有发生变化的资源。 如果用户再次访问了一个给定的URL(设置了ETag的资源),而且它是stale的(旧的:过于老导致不能使用),client会发送ETag的值在If-None-Match header上:
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
服务端回对比cleint通过If-None-Match发送过来的ETag,并且与当前版本的资源对比。 如果两个值比对上了(说明资源没有变化),server会发送一个304 Not Modified状态码,并且没有body,会告诉client:”response的缓存版本当前是可用的。(fresh)“
项目中的ETag分析
304
dist目录中的vender.xxx.js,位于云存储上。
// req header
if-none-match: "fafafaFkoHgQdfafafaC2xkHsKWPE8EvWnIDcq.gz"
// res header
etag: "fafafaFkoHgQdfafafaC2xkHsKWPE8EvWnIDcq.gz"
200
dist目录中的app.xxx.js,位于云存储上。
// req header
Provisional headers are shown
// res header
etag: "FumFsXBIhM_vEqYsfLGotbfshnIdxRcVfs8.gz"
disable cache与ETag
强验证。 关闭disable时:304。
// req header
if-none-match: "fafafaFkoHgQdfafafaC2xkHsKWPE8EvWnIDcq.gz"
// res header
etag: "fafafaFkoHgQdfafafaC2xkHsKWPE8EvWnIDcq.gz"
弱验证。 开启disable时:200。
// req header 没有if-none-match
cache-contorl: no-cache
// res header
etag: W/"sfFkYsIbsfafvg0crbkQpRWyzDWRPxFC9C"
参考资料:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag