HTTP 也是前端面试中常见的考察点, 了解这部分内容十分有必要。
常见的问题有:
为什么说 HTTPS 更安全
HTTP 1/2 有什么区别
304 是什么
option 请求是什么,每次都会发吗
描述一下密钥交换过程
...
这类问题都是 HTTP 相关的问题,十分常见。
下面我们就整体的回顾一下, 内容较多, 分成上下两部分。
今天的主要内容包括:
HTTP 是什么
HTTP 的前世今生
HTTP 的基础特性
基于 HTTP 的组件系统
HTTP 报文组成
HTTP 状态码
GET 和 Post的区别
HTTPS
HTTP/2 及 HTTP/3
HTTPS 及其工作流程
为何不所有的网站都使用HTTPS
HTTP 是什么
全称:超文本传输协议
(HyperText Transfer Protocol)
概念:HTTP 是一种能够获取像 HTML、图片等网络资源的通讯协议。
它是在 web 上进行数据交换的基础,是一种 client-server 协议。
HTTP 在因特网的角色:充当一个信使的角色,干的就是一个跑腿的活,在客户端和服务端之间传递信息,但我们又不能缺少它。
HTTP 协议是「 应用层 」协议,是与前端开发最息息相关的协议。
平时我们遇到的 HTTP 请求
、 HTTP 缓存
、Cookies
、跨域
等其实都跟 HTTP
息息相关。
HTTP(HyperText Transfer Protocol)是万维网(World Wide Web)的基础协议。
Tim Berners-Lee 博士和他的团队在1989-1991年间创造出它。
在 1991 年发布了 HTTP 0.9 版,在 1996 年发布 1.0 版,1997 年是 1.1 版,1.1 版也是到今天为止传输最广泛的版本。
2015 年发布了 2.0 版,其极大的优化了 HTTP/1.1 的性能和安全性,而 2018 年发布的 3.0 版,继续优化 HTTP/2,激进地使用 UDP 取代 TCP 协议。
目前,HTTP/3 在 2019 年 9 月 26 日 被 Chrome,Firefox,和 Cloudflare 支持。
单行协议,请求由单行指令构成。以唯一可用的方法 GET 开头。后面跟的是目标资源的路径
GET /mypage.html 响应:只包括响应文档本身
这是一个非常简单的HTML页面没有响应头,只传输 HTML 文件
没有状态码
RFC 1945 提出了 HTTP1.0,构建更好可拓展性
协议版本信息会随着每个请求发送。
引入了 HTTP 头的概念,无论是请求还是拓展,允许传输元数据。使协议变得灵活,更加具有拓展性
Content-Type
请求头,具备了传输除纯文本 HTML 文件以外其他类型文档的能力。
在响应中,Content-Type
标头告诉客户端实际返回的内容的内容类型
媒体类型
是一种标准。用来表示文档、文件或者字节流的性质和格式。
浏览器通常使用 MIME
(Multipurpose Internet Mail Extensions )类型来确定如何处理 URL,因此 Web 服务器在响应头中配置正确的 MIME 类型会非常的重要。
如果配置不正确,可能会导致网站无法正常的工作。
MIME
的组成结构非常简单: 由类型与子类型两个字符串中间用’/‘分隔而组成。
HTTP 从 MIME type 取了一部分来标记报文 body 部分的数据类型,这些类型体现在Content-Type 这个字段,当然这是针对于发送端而言,接收端想要收到特定类型的数据,也可以用 Accept 字段。
这两个字段的取值可以分为下面几类:
- text:text/html, text/plain, text/css 等
- image: image/gif, image/jpeg, image/png 等
- audio/video: audio/mpeg, video/mp4 等
- application: application/json, application/javascript, application/pdf, application/octet-stream
同时为了约定请求的数据和响应数据的压缩方式、支持语言、字符集等,还提出了以下的 Header:
压缩方式
:发送端
:Content-Encoding(服务端告知客户端,服务器对实体的主体部分的编码方式)接收端
:Accept-Encoding(用户代理支持的编码方式)
gzip
: 当今最流行的压缩格式;deflate
: 另外一种著名的压缩格式, 一种专门为 HTTP 发明的压缩算法。支持语言
Content-Language 和 Accept-Language(用户代理支持的自然语言集)
发送端:Content-Type 中,以 charset 属性指定。接收端:Accept-Charset(用户代理支持的字符集)。
// 发送端
Content-Encoding: gzip
Content-Language: zh-CN, zh, en
Content-Type: text/html; charset=utf-8
// 接收端
Accept-Encoding: gzip
Accept-Language: zh-CN, zh, en
Accept-Charset: charset=utf-8
虽然 HTTP1.0 在 HTTP 0.9 的基础上改进了很多,但还是存在这不少的缺点.
HTTP 最早期的模型,也是 HTTP/1.0 的默认模型,是短连接
。
每一个 HTTP 请求都由它自己独立的连接完成;
这意味着发起每一个 HTTP 请求之前, 都会有一次 TCP 握手,而且是连续不断的。
HTTP/1.1 在1997年1月以 RFC 2068 文件发布。
HTTP 1.1 消除了大量歧义内容并引入了多项技术
HTTP 1.1 支持长连接(PersistentConnection),在一个 TCP 连接上可以传送多个 HTTP 请求和响应,减少了建立和关闭连接的消耗和延迟。
在 HTTP1.1 中默认开启 Connection:keep-alive
,一定程度上弥补了 HTTP1.0 每次请求都要创建连接的缺点。
允许在第一个应答被完全发送完成之前就发送第二个请求,以降低通信延迟
。
复用同一个 TCP 连接期间,即便是通过管道同时发送了多个请求,服务端也是按请求的顺序依次给出响应的;
而客户端在未收到之前所发出所有请求的响应之前,将会阻塞后面的请求(排队等待),这称为队头堵塞
(Head-of-line blocking)。
分块编码传输:Transfer-Encoding: chunked
Content-length
声明本次响应的数据长度。
keep-alive
连接可以先后传送多个响应,因此用 Content-length 来区分数据包是属于哪一个响应。
使用 Content-Length 字段的前提条件是,服务器发送响应之前,必须知道响应的数据长度。
对于一些很耗时的动态操作来说,这意味着,服务器要等到所有操作完成,才能发送数据,显然这样的效率不高。
更好的处理方法是,产生一块数据,就发送一块,采用”流模式”(Stream)取代”缓存模式”(Buffer)。
因此,HTTP 1.1 规定可以不使用 Content-Length 字段,而使用”分块传输编码”(Chunked Transfer Encoding)。
只要请求或响应的头信息有 Transfer-Encoding: chunked 字段,就表明 body 将可能由数量未定的多个数据块组成。
每个数据块之前会有一行包含一个 16 进制数值,表示这个块的长度;
最后一个大小为 0 的块,就表示本次响应的数据发送完了。
在 HTTP1.0 中主要使用 header 里的 If-Modified-Since,Expires
等来做为缓存判断的标准.
HTTP1.1 则引入了更多的缓存控制策略例, 如 Entity tag, If-None-Match,Cache-Control
等更多可供选择的缓存头
来控制缓存策略
。
不同的域名配置同一个 IP 地址的服务器。
Host 是 HTTP 1.1 协议中新增的一个请求头,主要用来实现虚拟主机技术
。
虚拟主机(virtual hosting)即共享主机(shared web hosting),可以利用虚拟技术把一台完整的服务器分成若干个主机,因此可以在单一主机上运行多个网站或服务。
举个栗子,有一台 ip 地址为 61.135.169.125 的服务器,在这台服务器上部署着谷歌、百度、淘宝的网站。
为什么我们访问 https://www.google.com
时,看到的是 Google 的首页, 而不是百度或者淘宝的首页?
原因就是: Host 请求头决定着访问哪个虚拟主机
。
可拓展协议。
HTTP 1.0 出现的 HTTP headers 让协议拓展变得更加的容易。
只要服务端和客户端就 headers 达成语义一致,新功能就可以被轻松的加入进来。
HTTP 是无状态
的、有会话
的。
在同一个连接中,两个执行成功的 HTTP 请求之间是没有关系的。
这就带来了一个问题,用户没有办法在同一个网站中进行连续的交互,比如在一个电商网站里,用户把某个商品加入到购物车,切换一个页面后再次添加了商品,这两次添加商品的请求之间没有关联,浏览器无法知道用户最终选择了哪些商品。
而使用 HTTP 的头部扩展,HTTP Cookies
就可以解决这个问题。把 Cookies 添加到头部中,创建一个会话让每次请求都能共享相同的上下文信息,达成相同的状态。
HTTP 与连接
。通过 TCP,或者 TLS
——加密的 TCP 连接来发送,理论上任何可靠的传输协议都可以使用。连接是传输层
控制的,这从根本上来讲不是 HTTP 的范畴。
也就是说,HTTP 依赖于面向连接的 TCP 进行消息传递,但连接并不是必须的。
只需要它是可靠的,或不丢失消息的(至少返回错误)。
HTTP/1.0 默认为每一对 HTTP 请求/响应都打开一个单独的 TCP 连接。
当需要连续发起
多个请求时,这种模式比多个请求共享
同一个 TCP 链接更低效。
为此,HTTP 1.1 持久连接
的概念,底层 TCP 连接可以通过 connection
头部实现。
但 HTTP 1.1 在连接上也是不完美的,后面我们会提到。
HTTP 的组件系统包括客户端、web 服务器和代理
1. 客户端:user-agent
浏览器,特殊比如是工程师使用的程序,以及 Web 开发人员调试应用程序。
2. Web服务端
由 Web Server 来服务并提供客户端所请求的文档。
每一个发送到服务器的请求,都会被服务器处理并返回一个消息,也就是 response。
3. 代理(Proxy)
在浏览器和服务器之间,有很多计算机和其他设备转发了 HTTP 消息。
它们可能出现在传输层、网络层和物理层上,对于 HTTP 应用层而言就是透明的 有如下的一些作用:
即:由客户端发送用来触发一个服务器上的动作.
即:来自服务器端的应答。
HTTP 消息由采用 ASCII 编码的多行文本构成的。
在 HTTP/1.1 以及更早的版本中,这些消息通过连接公开的发送。
在 HTTP2.0 中,消息被分到了多个 HTTP 帧中。
通过配置文件(用于代理服务器或者服务器),API(用于浏览器)或者其他接口提供 HTTP 消息。
在客户端-服务器协议中,连接是由客户端发起建立的。
在 HTTP 中打开连接意味着在底层传输层启动连接,通常是 TCP。
使用 TCP 时,HTTP 服务器的默认端口号是 80,另外还有 8000 和 8080 也很常用.
HTTP 请求和响应都包括起始行(start line)、请求头(HTTP Headers)、空行(empty line)以及 body 部分,如下图所示:
请求的起始行:请求方法、请求 Path 和HTTP 版本号 响应的起始行:HTTP 版本号、响应状态码以及状态文本描述 下面详细说下请求 Path,请求路径(Path)有以下几种:
这是最常见的形式,称为 原始形式 (origin form)。
被 GET
,POST
,HEAD
和 OPTIONS
方法所使用:
POST / HTTP/1.1 GET /background.png HTTP/1.0 HEAD /test.html?query=alibaba HTTP/1.1 OPTIONS /anypage.html HTTP/1.0
主要在使用 GET 方法连接到代理的时候使用:
GET http://developer.mozilla.org/en-US/docs/Web/HTTP/Messages HTTP/1.1
仅在使用 CONNECT 建立 HTTP 隧道时才使用:
CONNECT developer.mozilla.org:80 HTTP/1.1
星号形式
(asterisk form)一个简单的星号(‘*‘),配合 OPTIONS 方法使用,代表整个服务器:
OPTIONS * HTTP/1.1 Headers 请求头或者响应头。详见下面的首部。不区分大小写的字符串,紧跟着的冒号 (‘:’) 和一个结构取决于 header 的值
空行
。很多人容易忽略Body
请求 Body 部分:
有些请求将数据发送到服务器以便更新数据:常见的的情况是 POST 请求(包含 HTML 表单数据)。
请求报文的 Body 一般为两类。
一类是通过 Content-Type
和 Content-Length
定义的单文件 body。
另外一类是由多Body
组成,通常是和 HTML Form 联系在一起的。
两者的不同表现在于 Content-Type
的值。
1)Content-Type —— application/x-www-form-urlencoded
对于 application/x-www-form-urlencoded 格式的表单内容。
有以下特点:
I. 其中的数据会被编码成以&分隔的键值对
II. 字符以URL编码方式编码。
转换过程: {a: 1, b: 2} -> a=1&b=2 -> 如下(最终形式) "a%3D1%26b%3D2"
2)Content-Type —— multipart/form-data
请求头中的 Content-Type 字段会包含 boundary,且 boundary 的值有浏览器默认指定。
例:
Content-Type: multipart/form-data;boundary=----WebkitFormBoundaryRRJKeWfHPGrS4LKe。
数据会分为多个部分,每两个部分之间通过分隔符来分隔,每部分表述均有 HTTP 头部描述子包体,如Content-Type,在最后的分隔符会加上—表示结束。
Content-Disposition: form-data;name="data1";
Content-Type: text/plain
data1
----WebkitFormBoundaryRRJKeWfHPGrS4LKe
Content-Disposition: form-data;name="data2";
Content-Type: text/plain
data2
----WebkitFormBoundaryRRJKeWfHPGrS4LKe--
响应 Body 部分:
1)由已知长度的单个文件组成。该类型 body 由两个 header 定义:Content-Type 和 Content-Length
2)由未知长度的单个文件组成,通过将 Transfer-Encoding 设置为 chunked 来使用 chunks 编码。关于 Content-Length 在下面 HTTP 1.0 中会提到,这个是 HTTP 1.0 中新增的非常重要的头部。
安全方法:HTTP 定义了一组被称为安全方法的方法。
GET
方法和 HEAD
方法都被认为是安全的,这意味着 GET 方法和 HEAD 方法都不会产生什么动作 —— HTTP 请求不会再服务端产生什么结果,但这并不意味着什么动作都没发生,其实这更多的是 web 开发者决定的.
GET
:请求服务器发送某个资源HEAD
:跟 GET 方法类似,但服务器在响应中只返回了首部。不会返回实体的主体部分。PUT
:向服务器中写入文档。语义:用请求的主体部分来创建一个由所请求的 URL 命名的新文档POST
:用来向服务器中输入数据的。通常我们提交表单数据给服务器。【POST 用于向服务器发送数据,PUT 方法用于向服务器上的资源(例如文件)中存储数据】TRACE
:主要用于诊断。实现沿通向目标资源的路径的消息环回(loop-back)测试 ,提供了一种实用的 debug 机制。OPTIONS
:请求 WEB 服务器告知其支持的各种功能。可以询问服务器支持哪些方法。或者针对某些特殊资源支持哪些方法。DELETE
:请求服务器删除请求 URL 中指定的的资源从很多资料我们可以了解到使用OPTIONS方法对服务器发起请求,可以检测服务器支持哪些 HTTP 方法。
但是这次我们并没有主动去发起 OPTIONS 请求,那OPTIONS请求为何会自动发起 ?
规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。
所以这个跨域请求触发了浏览器自动发起OPTIONS请求,看看此次跨域请求具体触发了哪些条件。
由于修改了Content-Type为application/json,触发了CORS预检请求。
可见一旦达到触发条件,跨域请求便会一直发送2次请求,这样增加的请求数是否可优化呢?答案是可以,OPTIONS预检请求的结果可以被缓存。
Access-Control-Max-Age这个响应首部表示 preflight request (预检请求)的返回结果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的信息) 可以被缓存的最长时间,单位是秒。(MDN)
如果值为 -1,则表示禁用缓存,每一次请求都需要提供预检请求,即用OPTIONS请求进行检测。
在其他场景,比如跨域并且业务有自定义请求头的话就很难避免了。
现在使用的axios或者superagent等第三方ajax插件,如果出现CORS预检请求,可以看看默认配置或者二次封装是否规范。
首先要了解下副作用
和幂等
的概念,副作用指的是: 对服务器端资源做修改
。
幂等
, 指发送 M 和 N 次请求(两者不相同且都大于 1),服务器上资源的状态一致
。
应用场景上:
技术上有以下的区分:
从 TCP 的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(火狐浏览器除外,它的 POST 请求只发一个 TCP 包)
101 Switching Protocols。在HTTP升级为WebSocket的时候,如果服务器同意变更,就会发送状态码 101。
200 OK,表示从客户端发来的请求在服务器端被正确处理
204 No content,表示请求成功,但响应报文不含实体的主体部分
205 Reset Content,表示请求成功,但响应报文不含实体的主体部分,但是与 204 响应不同在于要求请求方重置内容
206 Partial Content,进行范围请求
301 moved permanently,永久性重定向,表示资源已被分配了新的 URL
302 found,临时性重定向,表示资源临时被分配了新的 URL
303 see other,表示资源存在着另一个 URL,应使用 GET 方法获取资源
304 not modified,表示服务器允许访问资源,但因发生请求未满足条件的情况
307 temporary redirect,临时重定向,和302含义类似,但是期望客户端保持请求方法不变向新的地址发出请求
400 bad request,请求报文存在语法错误
401 unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息
403 forbidden,表示对请求资源的访问被服务器拒绝
404 not found,表示在服务器上没有找到请求的资源
500 internal sever error,表示服务器端在执行请求时发生了错误
501 Not Implemented,表示服务器不支持当前请求所需要的某个功能
503 service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求
HTTP Headers
1.通用首部(General headers)同时适用于请求和响应消息,但与最终消息主体中传输的数据无关的消息头。如 Date。
2.请求首部(Request headers)包含更多有关要获取的资源或客户端本身信息的消息头。
如 User-Agent
3.响应首部(Response headers)包含有关响应的补充信息
4.实体首部(Entity headers)含有关实体主体的更多信息,比如主体长(Content-Length)度或其 MIME 类型。如 Accept-Ranges。
详细的 Header 见 HTTP Headers 集合:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers
未完待续, 见下篇。