1. 浏览器缓存
一般来说,一个GET请求在目标资源存在的情况下会返回一个状态码为“200 OK”的响应,目标资源的内容将直接存放在响应报文的主体部分。如果资源的内容不会轻易改变,那么我们希望客户端(如浏览器)在本地缓存获取的资源。对于针对同一资源的后续请求来说,如果资源内容不曾改变,那么资源内容就无须再次作为网络荷载予以响应。
确定资源是否发生变化可以采用两种策略。第一种就是让资源的提供者记录最后一次更新资源的时间,资源的荷载内容(Payload)和这个时间戳将一并作为响应提供给作为请求发送者的客户端。客户端在缓存资源内容时也会保存这个时间戳。等到下次需要针对同一资源发送请求时,它会将这个时间戳一并发送出去,此时服务端就可以根据这个时间戳判断目标资源在上次响应之后是否被修改过,然后做出针对性的响应。第二种是针对资源的内容生成一个“标签”,标签的一致性体现了资源内容的一致性,在HTTP规范中将这个标签称为ETag(Entity Tag)。
2. If-Modified-Since与If-None-Match
对于HTTP请求来说,缓存资源携带的最后修改时间戳和ETag分别保存在名为If-Modified-Since与If-None-Match的报头中。报头名称体现的含义如下:只有目标资源在指定的时间之后被修改(If-Modified-Since)或者目前资源的状态与提供的ETag不匹配(If-None-Match)的情况下才会返回资源的荷载内容。
以Chrome为例, 当服务端接收到针对某个资源的GET请求时,如果请求不具有上述这两个报头或者根据这两个报头携带的信息判断资源已经发生改变,那么它返回一个状态码为“200 OK
”的响应。除了将资源内容作为响应主体,如果能够获取到该资源最后一次修改的时间(一般精确到秒),那么格式化的时间戳还会通过一个名为Last-Modified
的响应报头提供给客户端。针对资源自身内容生成的标签,则会以ETag响应报头的形式提供给客户端。反之,如果做出相反的判断,服务端就会返回一个状态码为“304 Not Modified
”的响应,这个响应不包含主体内容。一般来说,这样的响应也会携带Last-Modified
报头和ETag报头。
3. Chrome
从Chrome的Network请求中可以看到,
- 发送的请求的Header中包含了
If-Modified-Since
与If-None-Match
; - 返回的内容,同样会包含
Modified-Since
与ETag
; - 对于没有内容更改的请求,Status Code为
304 - Not Modified
- 对于304返回,
Modified-Since
与ETag
必然与请求的If-Modified-Since
与If-None-Match
分别一致;
4. 总结
- 客户端(浏览器)如果有缓存内容,发送请求时会在Header中包含
If-Modified-Since
与If-None-Match
- 当服务器内容没有修改时,服务器端会发送304给客户端,这是body内容可能为空;