美文网首页
HTTP特性总览

HTTP特性总览

作者: 落痕無情 | 来源:发表于2018-08-05 22:34 被阅读0次

    本文对HTTP协议的相关特性以及客户端-服务端请求-响应过程中的涉及的问题进行略为深入的讨论,也是参考慕课网课程《HTTP协议原理+实践》进行整理记录的一篇学习笔记。

    相关的示例代码:HTTP特性演示

    CORS跨域请求

    CORS跨域是由浏览器安全策略引起的,常用jsonp解决,也可以在服务端添加相应的允许跨域的header Access-Control-Allow-Origin,这样就可以直接使用ajax直接发起跨域请求。

    注意:

    • 不管服务端有没有设置header,浏览器都会正常发送跨域请求,服务端也会接受返回内容,如果没有设置 Access-Control-Allow-Origin,浏览器在解析内容后会自动拦截。这是浏览器的跨域限制问题,用curl工具就没关系。

    • 浏览器允许在html中使用 <script src=""> 标签来实现跨域请求,这也是jsonp的而基本原理。

    但是,也不是所有情况都可以仅仅通过设置一个header来解决问题,CORS跨域还有一些限制以及预请求验证功能。

    CORS中的限制:

    • 允许方法:

      • GET
      • HEAD
      • POST
    • 允许Content-Type:

      • text/plain
      • multipart/form-data
      • application/x-www-form-urlencoded
    • 其它限制

      • 自定义请求头限制

    其它情况下,必须发起预请求[OPTIONS方法]验证后才能正常发送,否则会报错。另外,可以设置 Access-Control-Max-Age 来限制预请求的时限,在这个时间内发起任何请求无需发起预请求的验证步骤。

    演示场景:

    使用浏览器从 localhost:8888localhost:8887 发起GET跨域请求,同时在服务端作了一定的预请求限制方案:

    client

    <script>
      fetch('http://localhost:8887', {
        method: 'POST',
        // 自定义header
        headers: {
          'X-Test-Cors': '123'
        }
      })
    </script>
    

    server

    response.writeHead(200, {
        'Access-Control-Allow-Origin': 'http://127.0.0.1:8888',
        'Access-Control-Allow-Headers': 'X-Test-Cors',
        'Access-Control-Allow-Methods': 'POST, PUT, DELETE',
        'Access-Control-Max-Age': '1000'
      })
    

    补充:关于 Fetch API

    缓存头Cache-Control

    可缓存性:

    • public: 任何地方都可以缓存

    • private: 只有发起请求的浏览器可以缓存

    • no-cache: 浏览器不允许缓存,但是代理服务器可以缓存

    • no-store: 任何地方都不能可以缓存

    设置缓存过期时间:

    • max-age=<seconds>: 浏览器缓存时间

    • s-maxage=<seconds>: 代理服务器缓存时间

    演示场景:

    在一个html文件中通过script标签去引用一个js文件,当客户端请求这个js文件路径时,服务端返回一段js代码,并对文件进行缓存。

    if (request.url === '/script.js') {
        response.writeHead(200, {
          'Content-Type': 'text/javascript',
          // 设置缓存头,value可以有多个,用逗号分隔
          'Cache-Control': 'max-age=20'
        })
        response.end('console.log("script loaded")')
      }
    

    第一次访问从服务器加载,在有效时间内再次访问会从直接缓存中读取,如果这期间修改了文件内容,也不会去加载更新的内容。这是由于设置了Cache-Control之后不会去经过服务端的验证。一般情况下,我们希望浏览器去缓存一些静态资源文件,提高页面加载速度,但是也要考虑资源更新的问题。

    注意:如果同时设置了 max-age 和 no-cache 之后,每一次浏览器发起请求,还是会先去服务端进行资源验证,验证之后如果确定这个资源可以使用缓存,才会去本地读取缓存,并不是直接从本地缓存中读取。

    前端如何解决浏览器长缓存问题?

    • 在打包静态资源的时候在对文件名加上一串哈希码,hash是通过文件内容进行计算的,如果内容有变化文件名也会随之改变,浏览器就会当成新的文件去请求。

    资源验证

    • Last-Modified 配合 If-Modified-Since 使用

    • Etag 配合 If-None-Match 使用

    如果对一个可缓存的文件设置了这两个Headers,那么在有效缓存实践内再次访问,客户端发起的请求头信息中就会新增两个Header,即 If-Modified-SinceIf-None-Match,它们的值分别为第一次访问时返回的 Last-ModifiedEtag 的值,作用是向服务端验证这两个缓存是否有效。

    但是这样设置仍然会从服务器上重新加载一次文件,而我们希望的是服务端告诉浏览器直接去读取本地缓存而已,这时候就需要去判断 If-None-Match 的值是否为 Etag 的值。

    if (request.url === '/script.js') {
      const etag = request.headers['if-none-match']
      if (etag === '777') {
        response.writeHead(304, {
          'Content-Type': 'text/javascript',
          'Cache-Control': 'max-age=2000000, no-cache',
          'Last-Modified': '123',
          'Etag': '777'
        })
        response.end()
      } else {
        response.writeHead(200, {
          'Content-Type': 'text/javascript',
          'Cache-Control': 'max-age=2000000, no-cache',
          'Last-Modified': '123',
          'Etag': '777'
        })
        response.end('console.log("script loaded twice")')
      }
    }
    

    注:304状态码的语义是 Not Modified

    nginx代理缓存配置

    nginx是一个单纯的http服务器,一般可以用于负载均衡代理缓存

    示例:

    proxy_cache_path cache levels=1:2 keys_zone=my_cache:10m;
    
    server {
      listen       80;
      server_name  test.com;
    
      location / {
        proxy_cache my_cache;
        proxy_pass http://127.0.0.1:8888;
        proxy_set_header Host $host;
      }
    }
    

    levels=1:2 在指cache目录下创建相应层级的子目录存放缓存文件,而不是全部堆积在根目录下。my_cache 是自定义缓存名,10m是允许最大缓存大小为10MB。

    如上,访问 test.com 后Nginx服务器会代理到访问 http://127.0.0.1:8888 启动的服务,同时会开启页面缓存,这份缓存是放在服务端的cache目录下,通过响应头 Cache-Controls-maxage 属性来控制过期时间。

    Cookie

    要使用cookie只要在服务端的响应头中添加 Set-Cookie 属性,里面可以设置多个cookie,以键值对的形式存在。cookie保存在浏览器中,下一次客户端访问同一个域名的服务器时,请求头中会自动带上之前获取到的cookie。

    Cookie属性:

    • max-ageexpires 设置过期时间

    • Secure 只在HTTPS的时候发送。

    • 设置了 HttpOnly 就无法通过 document.cookie 访问。

    实例:

    if (request.url === '/') {
      const html = fs.readFileSync('test.html', 'utf8')
      response.writeHead(200, {
        'Content-Type': 'text/html',
        'Set-Cookie': ['id=123; max-age=2', 'abc=456; domain=test.com']
      })
      response.end(html)
    }
    

    cookie只有在设置的域名下才可以访问,但也可以通过 domain 指定所有的二级域名也可以共享cookie。

    HTTP长连接

    Http 1.1 引入了长连接,请求头和响应头中都有 Connection: keep-alive

    浏览器在访问Web服务时会并发地创建一定数量的TCP连接(Chrome是6个),然后在这些TCP连接的基础上去发起http请求的三次握手,无论加载多少资源文件,都会去复用前面创建的TCP连接,但是是有先后顺序的。我们可以在Chrome调试工具中打开 Connection ID 项来查看是否复用了同一个TCP连接。

    Http 2 引入了信道复用,在TCP连接上可以并发地去发送http请求,也就是说连接网站时只需要1个TCP连接,减少了大量开销,整体访问速度会有很大提升。

    数据协商

    概念:在客户端发送给服务端一个请求时,客户端会声明这个请求的数据格式和相关限制,服务端后根据客户端的请求来区分应该返回怎样的数据。

    数据协商分为请求返回两部分。

    请求 - Accept

    设置 定义
    Accept 指定数据类型,根据 MIME Types 声明可接受的服务端返回的数据格式。
    Accept-Encoding 指定数据传输的编码方式,限制服务端如何进行数据压缩。
    Accept-Language 指定希望展示的语言
    User-Agent 表示浏览器的相关信息(如区分移动端或PC端浏览器……)

    Accept只是客户端希望服务端返回的方式(通常会列举很多种),实际上服务端不一定返回要求的格式。

    返回 - Content

    设置 定义
    Content-Type 声明服务端实际返回的数据格式
    Content-Encoding 声明压缩方式,如gzip
    Content-Language 是否返回了相关的语言
    const html = fs.readFileSync('test.html')
    response.writeHead(200, {
      'Content-Type': 'text/html',
      // 'X-Content-Options': 'nosniff'
      'Content-Encoding': 'gzip'
    })
    response.end(require('zlib').gzipSync(html))
    

    zlib压缩后传输的数据(包含内容和头信息)大小会变小,只是为了减少数据传输时的开销,但是解压出来的body数据的大小还是不变的。

    Html表单允许发送的 Content Type

    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded

    补充:用ajax发送带有文件的表单数据

    var form = document.getElementById('form')
    form.addEventListener('submit', function (e) {
      e.preventDefault()
      var formData = new FormData(form)
      fetch('/form', {
        method: 'POST',
        body: formData
      })
    })
    

    submit事件是绑定在 <form> 元素上的。

    重定向

    如果资源转移到了其他URL,则需要告诉客户端相应的目标路径并完成跳转:

    if (request.url === '/') {
      response.writeHead(302, {  // or 301
        'Location': '/new'
      })
      response.end()
    }
    if (request.url === '/new') {
      response.writeHead(200, {
        'Content-Type': 'text/html',
      })
      response.end('<div>this is content</div>')
    }
    

    说明:

    • 浏览器默认302跳转,302是临时重定向,每次访问都会经过服务器的跳转过程到达新的URL。

    • 301是永久重定向,告诉浏览器下一次直接访问新的URL即可,实际上是放到了浏览器的缓存中直接读取,所以301不能反悔,因为用户浏览器的缓存是不可控的。

    • 301重定向将SEO评分从旧地址直接转移到新地址,302重定向会被认为作弊。

    CSP

    CSP的全称为Content-Security-Policy,即“内容安全策略”。

    作用:

    • 限制资源获取
    • 报告资源获取越权

    相关文章

      网友评论

          本文标题:HTTP特性总览

          本文链接:https://www.haomeiwen.com/subject/ufuivftx.html