HTTP概述
超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一种能够获取如 HTML 这样的网络资源的通讯协议。它是在 Web 上进行数据交换的基础,是一种 client-server 协议,也就是说,请求通常是由像浏览器这样的接受方发起的。一个完整的Web文档通常是由不同的子文档拼接而成的,像是文本、布局描述、图片、视频、脚本等等。
HTTP 报文
HTTP/1.1以及更早的HTTP协议报文都是语义可读的。在HTTP/2中,这些报文被嵌入到了一个新的二进制结构,帧。帧允许实现很多优化,比如报文头部的压缩和复用。即使只有原始HTTP报文的一部分以HTTP/2发送出来,每条报文的语义依旧不变,客户端会重组原始HTTP/1.1请求。因此用HTTP/1.1格式来理解HTTP/2报文仍旧有效。
有两种HTTP报文的类型,请求与响应,每种都有其特定的格式。
请求
HTTP请求的一个例子:
请求由以下元素组成:
- 一个HTTP的
method,经常是由一个动词像GET,POST或者一个名词像OPTIONS,HEAD来定义客户端的动作行为。通常客户端的操作都是获取资源(GET方法)或者发送HTML表单(POST方法)等。 - 要获取的资源的路径,通常是上下文中就很明显的元素资源的URL
- HTTP协议版本号。
- 为服务端表达其他信息的可选头部
headers。 - 对于一些像POST这样的方法,报文的body就包含了发送的资源,这与响应报文的body类似。
响应
HTTP响应的一个例子:
响应报文包含了下面的元素:
- HTTP协议版本号。
- 一个状态码(
status code),来告知对应请求执行成功或失败,以及失败的原因。 - 一个状态信息,这个信息是非权威的状态码描述信息,可以由服务端自行设定。
- HTTP
headers,与请求头部类似。 - 可选项,比起请求报文,响应报文中更常见地包含获取的资源body。
HTTP Headers
HTTP 消息头允许客户端和服务器通过 request 和 response 传递附加信息。一个请求头由名称(不区分大小写)后跟一个冒号“:”,冒号后跟具体的值(不带换行符)组成。
这里接收端就是浏览器客户端,发送端就是服务器
Accept(接受端)和Content-Type(发送端)
用户希望接收的媒体资源的 MIME 类型
- 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
- application/x-www-form-urlencoded : <form encType=>中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
- multipart/form-data : 需要在表单中进行
文件上传时,就需要使用该格式
Accept-Encoding(接收端)和Content-Encoding(发送端)
支持的压缩方法
- gzip: 当今最流行的压缩格式
- deflate: 另外一种著名的压缩格式
- br: 一种专门为 HTTP 发明的压缩算法
Accept-Language(接收端)和Content-Language(发送端)
期望的页面语言
zh-CN, zh, en
Accept-Charset(接收端)和Content-Type(发送端)
支持的字符集
Accept-Charset: charset=utf-8
Content-Type: text/html; charset=utf-8
HTTP 请求方法
GET:请求一个指定资源的表示形式,使用GET的请求应该只被用于获取数据。HEAD:请求一个与GET请求的响应相同的响应,但没有响应体。POST:用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用。PUT:用请求有效载荷替换目标资源的所有当前表示。DELETE:删除指定的资源。CONNECT:建立一个到由目标资源标识的服务器的隧道。OPTIONS:用于描述目标资源的通信选项。跨域请求先发送一次,返回成功后进行真正的请求。TRACE:沿着到目标资源的路径执行一个消息环回测试。PATCH:用于对资源应用部分修改
GET和POST区别
- 缓存:GET会被服务器主动缓存,POST不会
- 数据:GET数据放在URL中,长度限制2048个字符,限制ASCII字符,POST数据放在请求体中,长度和类型都没限制
- 安全性:POST相对来说比GET安全,GET数据暴露在URL中,而POST放在请求体中。
切记:GET和POST使用的HTTP是明文传输,都可以通过抓包获取到数据,只有通过加密才是安全的,例如HTTPS
HTTP 状态码
- 1xx - 服务器收到请求。
- 2xx - 请求成功,如 200。
- 3xx - 重定向,资源位置发生变动,需要重新请求,如 302。
- 4xx - 客户端错误,请求有误,如 404。
- 5xx - 服务端错误,如 500。
1xx(信息响应)
- 100 Continue 这个临时响应表明,迄今为止的所有内容都是可行的,客户端应该继续请求,如果已经完成,则忽略它。
2xx(成功响应)
- 200 OK 请求成功。成功的含义取决于HTTP方法
- 201 Created 该请求已成功,并因此创建了一个新的资源。这通常是在POST请求,或是某些PUT请求之后返回的响应。
- 202 Accepted 请求已经接收到,但还未响应,没有结果。意味着不会有一个异步的响应去表明当前请求的结果,预期另外的进程和服务去处理请求,或者批处理。
- 204 No Content 服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息。
- 206 Partial Content 服务器已经成功处理了部分 GET 请求。类似于 FlashGet 或者迅雷这类的 HTTP 下载工具都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。该请求必须包含 Range 头信息来指示客户端希望得到的内容范围,并且可能包含 If-Range 来作为请求条件。
3xx(重定向)
- 300 Multiple Choice 被请求的资源有一系列可供选择的回馈信息,每个都有自己特定的地址和浏览器驱动的商议信息。用户或浏览器能够自行选择一个首选的地址进行重定向。
- 301 Moved Permanently 被请求的资源已
永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。除非额外指定,否则这个响应也是可缓存的。 - 302 Found 请求的资源现在
临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。 - 304 Not Modified 如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。
当协商缓存命中时会返回这个状态码
4xx(客户端响应)
- 400 Bad Request 1、语义有误,当前请求无法被服务器理解。2、请求参数有误。
- 401 Unauthorized 当前请求需要用户验证。也就是请求没有权限。
- 403 Forbidden 服务器已经理解请求,但是拒绝执行它。
- 404 Not Found 请求失败,请求所希望得到的资源未被在服务器上发现。例如访问的路由错误。
- 405 Method Not Allowed 请求行中指定的请求方法不能被用于请求相应的资源。
- 408 Request Timeout 请求超时。客户端没有在服务器预备等待的时间内完成一个请求的发送。客户端可以随时再次提交这一请求而无需进行任何更改。
5xx(服务端响应)
- 500 Internal Server Error 服务器遇到了不知道如何处理的情况。
- 501 Not Implemented 此请求方法不被服务器支持且无法被处理。只有
GET和HEAD是要求服务器支持的,它们必定不会返回此错误代码。 - 502 Bad Gateway 此错误响应表明服务器作为网关需要得到一个处理这个请求的响应,但是得到一个错误的响应。
- 503 Service Unavailable 服务器没有准备好处理请求。
HTTP 中如何处理表单数据的提交
在 http 中,发送请求时有两种主要的表单提交方式,区分方式在前面提过的消息头中提到的Content-Type属性:
- application/x-www-form-urlencoded
- multipart/form-data
application/x-www-form-urlencoded
- 其中的数据会被编码成以
&分隔的键值对 - 字符以URL编码方式编码
multipart/form-data
- 请求头中的
Content-Type字段会包含boundary,且boundary的值有浏览器默认指定。
Content-Type: multipart/form-data;boundary=----WebkitFormBoundaryRRJKeWfHPGrS4LKe
-
数据会分为多个部分,每两个部分之间通过分隔符来分隔,每部分表述均有 HTTP 头部描述子包体,如
Content-Type,在最后的分隔符会加上--表示结束。 -
常用于文件或图片上传的格式
HTTP缓存
http缓存: 当客户端向服务器请求资源时,会先抵达浏览器缓存,如果浏览器有“要请求资源”的副本,就可以直接从浏览器缓存中提取而不是从原始服务器中提取这个资源。
常见的http缓存只能缓存GET请求响应的资源,对于其他类型的响应则无能为力,所以后续说的请求缓存都是指GET请求。
缓存的分类
根据是否需要重新向服务器发起请求来分类,可分为(强制缓存,协商缓存) 根据是否可以被单个或者多个用户使用来分类,可分为(私有缓存,共享缓存) 强制缓存如果生效,不需要再和服务器发生交互,而协商缓存不管是否生效,都需要与服务端发生交互
强制缓存
强制缓存在缓存数据未失效的情况下(即Cache-Control的max-age没有过期或者Expires的缓存时间没有过期),那么就会直接使用浏览器的缓存数据,不会再向服务器发送任何请求。强制缓存生效时,http状态码为200。这种方式页面的加载速度是最快的,性能也是很好的,但是在这期间,如果服务器端的资源修改了,页面上是拿不到的,因为它不会再向服务器发请求了。这种情况就是我们在开发种经常遇到的,比如你修改了页面上的某个样式,在页面上刷新了但没有生效,因为走的是强缓存,所以Ctrl + F5一顿操作之后就好了。跟强制缓存相关的header头属性有(Pragma/Cache-Control/Expires)
协商缓存
当第一次请求时服务器返回的响应头中没有Cache-Control和Expires或者Cache-Control和Expires过期还或者它的属性设置为no-cache时(即不走强缓存),那么浏览器第二次请求时就会与服务器进行协商,与服务器端对比判断资源是否进行了修改更新。如果服务器端的资源没有修改,那么就会返回304状态码,告诉浏览器可以使用缓存中的数据,这样就减少了服务器的数据传输压力。如果数据有更新就会返回200状态码,服务器就会返回更新后的资源并且将缓存信息一起返回。跟协商缓存相关的header头属性有(ETag/If-Not-Match 、Last-Modified/If-Modified-Since)请求头和响应头需要成对出现
当浏览器第一次向服务器发送请求时,会在响应头中返回协商缓存的头属性:ETag和Last-Modified,其中ETag返回的是一个hash值,Last-Modified返回的是GMT格式的最后修改时间。然后浏览器在第二次发送请求的时候,会在请求头中带上与ETag对应的If-Not-Match,其值就是响应头中返回的ETag的值,Last-Modified对应的If-Modified-Since。服务器在接收到这两个参数后会做比较,如果返回的是304状态码,则说明请求的资源没有修改,浏览器可以直接在缓存中取数据,否则,服务器会直接返回数据。
跨域
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域;
非同源限制:
- 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
- 无法接触非同源网页的 DOM
- 无法向非同源地址发送 AJAX 请求
解决方法
- JSONP
JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),缺点是只支持get请求,不支持post请求。
<script src="http://test.com/data.php?callback=dosomething"></script>
// 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字
// 处理服务器返回回调函数的数据
<script type="text/javascript">
function dosomething(res){
// 处理获得的数据
console.log(res.data)
}
</script>
- CORS
cors(跨源资源共享)是一种基于HTTP头的机制,允许服务器标示除了它自己以外的其他源(域、协议、端口)有权限访问哪些资源。另外规范要求,对那些可能对服务器产生副作用的HTTP请求方法,浏览器必须首先使用
OPTIONS方法发起一个预检请求,从而获知服务端是否允许该夸源请求。服务器允许后,才发起实际的HTTP请求。GET,POST,HEAD不会触发OPTIONS方法发起一个预检请求。
服务端设置:
// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
response.setHeader("Access-Control-Allow-Origin", "http://www.test.com");
// 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示
response.setHeader("Access-Control-Allow-Credentials", "true");
// 提示OPTIONS预检时,后端需要设置的两个常用自定义头
response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With")
- Proxy本地代理
在webpack.config.js中利用 WebpackDevServer 配置本地代理
devServer: {
port: 8080,
proxy: {
"/api": {
target: "http://192.168.25.20:8088"
// http://localhost:8080/api/getData的请求就会变为http://192.168.25.20:8088/getData
}
}
}
- Nginx反向代理
正向代理帮助客户端访问客户端自己访问不到的服务器,然后将结果返回给客户端。
反向代理拿到客户端的请求,将请求转发给其他的服务器,主要的场景是维持服务器集群的负载均衡,换句话说,反向代理帮其它的服务器拿到请求,然后选择一个合适的服务器,将请求转交给它。
因此,两者的区别就很明显了,正向代理服务器是帮客户端做事情,而反向代理服务器是帮其它的服务器做事情。
比如说现在客户端的域名为client.com,服务器的域名为server.com,客户端向服务器发送 Ajax 请求,当然会跨域了,那这个时候让 Nginx 登场了,通过下面这个配置:
server {
listen 8080;
server_name client.com;
#client.com:8080 会被转发到这里
#同时, 服务端程序会接收到 "server.com:8088"这样的请求url
location / {
proxy_pass server.com:8088;
}
#client.com:8080/api/ 会被转发到这里
#同时, 服务端程序会接收到 "server.com:8080/api/"这样的请求url
location /api/ {
proxy_pass server.com:8080;
}
}
HTTP和HTTPS
HTTP 是明文传输的协议,传输报文对外完全透明,非常不安全,于是出现了HTTPS协议,它是HTTP协议和SSL/TSL协议加密组合而成,HTTPS = HTTP + SSL/TLS。
SSL(Secure Socket Layer,安全套接字层)由Netscape开发,经过不断的更新迭代,发展到SSL3.0版本,随后被被 IETF 标准化并改名TSL,所有TLS1.0 = SSL3.1,目前TSL发展到TSL1.3,使用最广泛的还是TSL1.2
区别
- HTTPS协议需要到CA申请证书,一般免费证书较少,因而需要一定费用,HTTP完全免费。
- HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的SSL/TSL加密传输协议。
- HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
- HTTP的连接很简单,是无状态的;HTTPS协议是由SSL/TSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP协议安全。
加密
大致加密过程看下图:
客户端和服务端之间的加密机制:
TLS协议是基于TCP协议之上的,图中第一个蓝色往返是TCP的握手过程,之后两次橙色的往返,我们可以叫做TLS的握手。握手过程如下:
- client1:TLS版本号+所支持加密套件列表+希望使用的TLS选项
- Server1:选择一个客户端的加密套件+自己的公钥+自己的证书+希望使用的TLS选项+(要求客户端证书);
- Client2:(自己的证书)+使用服务器公钥和协商的加密套件加密一个对称秘钥(自己生成的一个随机值);
- Server2:使用私钥解密出对称秘钥(随机值)后,发送加密的Finish消息,表明完成握手
说到加密就会涉及两种不同的加密:对称加密 和 非对称加密
对称加密
encrypt(明文,秘钥) = 密文
decrypt(密文,秘钥) = 明文
这种加密方式就好比:我们把一个需要加密的东西放在一个箱子锁起来给别人,那么接收的人要打开箱子就要我们锁箱子的钥匙,那么我们把钥匙一起发给别人就可能被别人半路偷了,就不安全了。
非对称加密
encrypt(明文,公钥) = 密文
decrypt(密文,私钥) = 明文
这种加密方式就好比:我们把一个需要加密的东西放在一个箱子锁起来,首先问别人要一个锁的图纸,别人手里有这个锁的钥匙,然后我们拿到图纸把锁造出来,锁好箱子发给别人,最后接收人拿到箱子用锁的钥匙打开箱子拿到东西。这样我们箱子半路被偷了也拿不到我们里面的东西。
最后HTTPS为了追求性能,又要保证安全,采用了共享密钥加密和公开密钥加密混合的方式进行报文传输。
就好比是:我们用对称加密的方式,把我们需要发送的东西锁在箱子A里,然后通过非对称加密的方式,问别人要一个锁的图纸用箱子B把箱子A的钥匙锁起来,最后把箱子A,B都发给别人,接收的人通过箱子B的钥匙打开箱子拿到里面箱子A的钥匙,然后用箱子A的钥匙就能打开箱子A了。这样以后发送东西都用箱子A锁好了发就行,不需要每次造个钥匙去锁箱子在发,效率大大提高了。
HTTP1.0和HTTP1.1和HTTP2.0的区别
HTTP1.0和HTTP1.1的区别
- 缓存策略:
http1.0的缓存策略主要是依赖header中的If-Modiified-Since,Expire(到期) http1.1的缓存策略要比http1.0略多,例如 Entity tag(实体标签), If-Unmodified-Since, If-Match, If-None-Match等.
-
长连接 HTTP1.1支持长连接和请求的流水线处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启长连接keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。HTTP1.0需要使用keep-alive参数来告知服务器端要建立一个长连接。
-
带宽 HTTP1.0发送完整信息,
不支持断点续传。
HTTP1.1可以只发送请求的header部分,服务器通过了,再发送请求的body,节省了带宽。
- Host
http1.0中默认每台服务器都绑定唯一的一个IP地址,所以请求消息中url并没有传递主机名,也就是hostname.
http1.1中请求消息和响应消息都支持Host头域,而且,如果我们不传这个字段还会报一个400(bad request)的状态码
HTTP2.0和HTTP1.x的区别
- 解析
http1的解析是基于文本协议的各式解析,而http2.0的协议解析是二进制格式,更加的强大
- 多路复用
HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。HTTP1.1也可以多建立几个TCP连接,来支持处理更多并发的请求,但是创建TCP连接本身也是有开销的。
- 头部压缩
在 HTTP1.1 及之前,请求体一般会有响应的压缩编码过程,但请求头没有
HTTP2.0 针对头部字段,也采用了对应的压缩算法——HPACK,对请求头进行压缩。
- 服务器推送
在 HTTP/2 当中,服务器已经不再是完全被动地接收请求,响应请求,它也能新建 stream 来给客户端发送消息,当 TCP 连接建立之后,比如浏览器请求一个 HTML 文件,服务器就可以在返回 HTML 的基础上,将 HTML 中引用到的其他资源文件一起返回给客户端,减少客户端的等待。
参考资料
developer.mozilla.org/zh-CN/docs/… juejin.cn/post/684490… juejin.im/entry/5981c…