express-强缓存与协商缓存

Posted by XuBaoshi on August 17, 2017

express-强缓存与协商缓存

静态资源的强缓存及协商缓存

强缓存 200-from-cache

强缓存是利用Expires或者Cache-Control实现的,它们都表示资源在客户端缓存的有效时间Expires是http1.0提出的一个表示资源过期时间的header,描述的是一个绝对时间由服务器返回,GMT格式的字符串表示,如:Expires:Thu, 31 Dec 2037 23:55:55 GMT,当浏览器对某个资源的请求命中了强缓存时,返回的http状态200,chrome f12查看network中size会显示为 from cache。

/img/express-cache/cache-control-1.jpg

Expires缓存过程

  • 浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Expires的header。 /img/express-cache/expires1.png
  • 浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来。
  • 浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,拿出它的Expires跟当前的请求时间比较,如果请求时间在Expires指定的时间之前,命中缓存。
  • 如果缓存没有命中,浏览器直接从服务器加载资源时,Expires Header在重新加载的时候会被更新。

    Cache-Control缓存过程

  • 浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Cache-Control的header。 /img/express-cache/cache-control-2.png
  • 浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来。
  • 浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,根据它第一次的请求时间和Cache-Control设定的有效期,计算出一个资源过期时间,再拿这个过期时间跟当前的请求时间比较,如果请求时间在过期时间之前,就能命中缓存。

Expires与Cache-Control 区别

Expires是一个较老的强缓存管理的header,由于它是服务器返回的一个绝对时间,如果服务器时间与客户端时间相差较大时,缓存管理容易出现问题。如:修改本地时间可以影响缓存的命中。而Cache-Control描述的是一个相对时间(max-age),在缓存命中的时候,都是利用客户端的时间进行判断,相对较Expires有效可靠一些。
这两个header可以只启用一个,或者可以同时启用,如果同时启用优先使用Cache-Control。

协商缓存 304-not-modified

当浏览器对某个资源的请求没有命中强缓存,就就发一个请求到服务器,验证协商缓存是否命中,如果协商缓存命中,响应返回的http状态码304并且会显示一个Not Modified的字符串。

协商缓存是利用的是 【Last-Modified,If-Modified-Since】和 【ETag,If-None-Match】这两对Header来管理的。

【Last-Modified,If-Modified-Since】缓存过程

  • 浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Last-Modified的header,这个header表示这个资源在服务器上的最后修改时间。 /img/express-cache/modified1.jpg
  • 浏览器再次跟服务器请求这个资源时,在request的header上加上If-Modified-Since的header,这个header的值就是上一次请求时返回的Last-Modified的值。如果没有变化则返回304 Not Modified,但是不会返回资源内容。 /img/express-cache/modified2.jpg
  • 浏览器收到304的响应后,资源内容直接从本地请求。
  • 如果协商缓存没有命中,浏览器直接从服务器加载资源时,Last-Modified Header在重新加载的时候会被更新。

    【ETag,If-None-Match】缓存过程

  • 浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上ETag的header,这个header是服务器根据当前请求的资源生成的一个唯一标识,这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间没有关系。 /img/express-cache/etag1.jpg
  • 浏览器再次跟服务器请求这个资源时,在request的header上加上If-None-Match的header,这个header的值就是上一次请求时返回的ETag的值。 /img/express-cache/etag2.jpg
  • 服务器再次收到资源请求时,根据浏览器传过来If-None-Match和然后再根据资源生成一个新的ETag,如果这两个值相同就说明资源没有变化,否则就是有变化;如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。
  • 浏览器收到304的响应后,就会从缓存中加载资源。

    【Last-Modified,If-Modified-Since】与【ETag,If-None-Match】区别

    【Last-Modified,If-Modified-Since】都是根据服务器时间返回的header,一般来说,在没有调整服务器时间和篡改客户端缓存的情况下,这两个header配合起来管理协商缓存是非常可靠的,但是有时候也会服务器上资源其实有变化,但是最后修改时间却没有变化的情况,而当这种情况出现的时候,就会影响协商缓存的可靠性。【ETag,If-None-Match】则是根据请求的资源作为依据可靠性会好一些。

express.static

4.x 版本 Express已经不再依赖Connect,除了express.static,Express以前内置的中间件现在已经全部单独作为模块安装使用。express.static 是Express唯一内置的中间件,用于托管静态资源。

// root提供静态资源的根目录 使用方法: express.static(root,[options]) options属性如下:

  • dotfiles 是否对外输出文件名以(.)开头的文件 默认值:’ignore’, 其他可选值:’allow’、’deny’
  • etag 是否启用etag生成 默认值:true
  • extensions 设置扩展名备份选项 默认值:[]
  • index 发送目录索引文件,设置为false禁用 默认值:”index.html”
  • lastModified 设置Last-Modified 头文件在操作系统上的最后修改日期 默认值:true
  • maxAge 以毫秒或者其字符串格式设置 Cache-Control头的max-age属性 默认值:0
  • redirect 当路径为目录时,重定向至”/”
  • setHeaders 设置HTTP头已提供文件的函数

      var express = require('express');
      var app = express();
      app.get('/expires-p', function (req, res) {
          res.sendFile(__dirname + '/expires.html')
      });
      app.get('/cache-p',function(req,res){
          res.sendFile(__dirname + '/cache.html')
      });
      app.get('/etag-p',function(req,res){
          res.sendFile(__dirname + '/etag.html')
      });
      app.get('/modified-p',function(req,res){
          res.sendFile(__dirname + '/modified.html')
      });
      app.use(express.static('expires', {
          dotfiles: 'ignore',
          etag: false,
          extensions: ['html', 'htm', 'css', 'png', 'gif', 'jpg', 'js', 'tpl'],
          index: 'index.html',
          lastModified: false,
          maxAge: 0,
          redirect: true,
          setHeaders: function (res,path,stat) {
              res.setHeader('Expires', new Date(Date.now() + 345600000).toUTCString());
          }
      }));
      app.use(express.static('cache-control', {
          dotfiles: 'ignore',
          etag: false,
          extensions: ['html', 'htm', 'css', 'png', 'gif', 'jpg', 'js', 'tpl'],
          index: 'index.html',
          lastModified: false,
          maxAge: '10000',
          redirect: true
      }));
      app.use(express.static('etag', {
          dotfiles: 'ignore',
          etag: true,
          extensions: ['html', 'htm', 'css', 'png', 'gif', 'jpg', 'js', 'tpl'],
          index: 'index.html',
          lastModified: false,
          maxAge: 0,
          redirect: true
      }));
      app.use(express.static('modified', {
          dotfiles: 'ignore',
          etag: false,
          extensions: ['html', 'htm', 'css', 'png', 'gif', 'jpg', 'js', 'tpl'],
          index: 'index.html',
          lastModified: true,
          maxAge: 0,
          redirect: true
      }));
      var server = app.listen(3000, '', function () {
          var host = server.address().address;
          var port = server.address().port;
      });