HTTP请求详解
1.认识方法
方法是HTTP中非常重要的部分,可以把方法理解成你这个请求想干什么。
1.1 方法的种类
这里面GET和POST是最常用的方法。
1.2 产生GET请求的途径
- 浏览器地址栏直接输入URL,会触发GET请求。
- html里面的link,a,img,script标签也会触发GET请求。(href或src都会引用一个外部资源)
- html里的form表单可以构造出GET请求。
- ajax也可以构造GET请求。
1.3 输入网址后fiddler为什么会抓到那么多的请求
输入sogou网址后,通过抓包工具获取到了很多请求,这是因为一个浏览器加载出一个界面,要经过多重HTTP请求的交互,浏览器在加载页面的时候,往往要加载这个页面依赖的很多其他资源,如css,js,图片啥的,加载这些资源,显然要消耗不少时间。为了提高页面加载效率,浏览器就会对加载过的这些css,js,图片进行缓存(保存在你的本地磁盘上),下次再访问同一个网页之前的css,js,图片,就不必重新从网络加载,而是直接读硬盘即可。
使用ctrl+f5可以强制刷新,就可以让浏览器不走缓存,直接强制从网络上获取资源。
1.4 网上的一种说法:GET请求最多是1KB/2KB/1MB等等。
这种说法是不准确的,HTTP标准协议中并没有谈到长度上限,但是浏览器和HTTP服务器在实现的时候,可能是由长度上限的,也可能是没有的。(取决于具体的实现)
产生POST请求的途径~
1.form表单
2.ajax
1.5 GET请求的特点
通过fiddler抓包得到下面的GET请求,根据报文内容我们可以简单总结出GET请求的特点
- 首行的第一部分是GET
- URL的query string可以为空,也可以不为空
- header部分有若干个键值对
- body部分为空
分割线,下面内容为POST请求的内容
1.6 POST请求的特点
通过fiddler抓包得到下面的POST请求,根据报文内容我们可以简单总结出POST请求的特点
- 首行的第一部分为POST
- url通常是没有query string的(也可以有query string,但是很少会这样)
- 也有若干header,以键值对的形式
- body这里通常是有的,一般不为空,body的数据格式有很多种
- body内的数据格式通过header中的content-type指定,body的长度由header中的content-length指定
- POST在传递消息给服务器的时候,通常就会把信息放到body中
1.7 GET和POST的区别(经典题目,重点!)
- GET和POST没有本质区别,使用GET实现的场景,基本都可以使用POST代替。使用POST实现的场景,也可以用GET来代替
- GET的语义,是“从服务器获取个数据”,POST的语义,是“往服务器上提交个数据”,这些是HTTP协议设计的“初心”,但是实践中,很多程序员并没有遵守这个
- 给服务器传递的数据,GET通常放在url的query string中,POST通常放在body中
- GET请求建议实现成“幂等”的,POST则一般不要求实现幂等
- 在幂等的基础上,GET的请求结果是可以被缓存的,POST则一般不会缓存
幂等是数学上的术语,简单来说就是输入确定,则输出也是确定的。
如牛每天吃草,每天挤出来的都是奶,称作幂等。
但如果牛第一天吃草,挤出来奶,第二天还是吃草,挤出来的是百事可乐了,那么就不是幂等了。
GET的请求结果是可以被缓存的:这是浏览器的默认行为,如果当前GET确实是幂等的,就不必处理,就让浏览器缓存,没有问题。如果当前GET不是幂等的,就需要通过特殊技巧避免浏览器产生缓存。(典型的技巧就是让每次GET请求的URL都不相同(通过特殊的query string来保证URL不同))
1.8 POST比GET更安全?
答案是否定的!网上能搜到这么个说法,依据是GET把参数放在URL中,登录页面时能看到账号密码,POST把参数放在body中,不会显示在URL上。
虽然把账号密码放在URL中并不安全,但是POST也并没有多么安全。通过抓包工具就能很轻易的得到POST请求中的body的内容。我们平时谈的安全,一般是数据被黑客接货之后,不会对你造成信息泄露这样的影响,只要你的数据没有被加密,那么就谈不上安全。数据被加密后,才能称得上安全。
2.认识请求“报头”
header的整体的格式为键值对格式,每个键值对占一行,键和值之间用 “:” 分割。
这里的键值对都是标准规定的,有特定含义的,不过这里也可以放一些自定义的键值对。
报头里的键值对很多,此处仅介绍常见的几个。
使用fiddler抓了个包,可以看到有很多的键值对。
2.1 Host
Host表示服务器主机的地址和端口,也就是表示去哪里找到服务器。
Host放的是ip+端口号,这里的端口号可以省略,省略表示默认值。HTTP的默认值是80,HTTPS的默认值是443。
URL里面不是就有ip和端口号吗,为什么还要搞个host放ip+端口号?
事实上,URL里的IP+端口号与host里的ip+端口号不一定完全一样,当请求是经过代理来访问的时候,是可能会不一样的,不过这一点在fiddler里面没有提现出来。
2.2 Content-Length
表示body中的数据长度。
HTTP协议,在传输层是基于TCP实现的。TCP是面向字节流的,所以存在粘包问题。(HTTP3.0之前是基于TCP实现的,从3.0开始变成了UDP)
解决粘包问题的核心思路有两个
- 约定一个分隔符,这个在HTTP中有体现,就是HTTP报文中的空行
- 约定报文长度,也就是Content-Length
2.3 Content-Type
表示请求的body中的数据格式。body中的数据格式可以为很多种,存不同的格式,对于接收方来说,解析方式是截然不同的
Content-Type常见的值
- application/x-www-form-urlencodedform ===> 如果是form表单构造的请求,就是这个值
- multipart/form-data ===> 这个格式主要是上传文件的时候会出现
- application/json ===> body里的数据为json格式,Content-Type会是这个值
Content-Length和Content-Type这俩字段不一定有,但如果有一个,另一个也必定有。
也就是说如果请求没有body(GET),就没有这俩字段
如果请求有body(POST),一定有这俩字段
2.4 User - Agent(简称UA)
表示浏览器/操作系统的属性。Mozilla/5.0是一个开源组织
UA主要包含的信息就是操作系统信息和浏览器信息,描述了用户在使用什么样的设备上网。
随着浏览器的发展和“响应式页面的出现”,UA的作用相比于几年前已大大削弱,失去了最初的作用。在现在大数据时代背景下,UA的主要作用是在服务器端统计用户的设备情况
2.5 Referer
表示当前这个页面是从哪跳转过来的。(也就是说上级页面是啥)
在浏览器上直接输入URL或者从收藏夹打开一个网页,都是没有Referer的
从搜狗输入框里输入“不孕不育”,之后打开fiddler,再点击回车,新打开页面的报文中就会有下面这个Referer
2.6 Cookie
cookie是浏览器在本地存储数据的一种机制。
Cookie是Cookie,缓存是缓存,Cookie是持久化存储的一种手段,跟缓存不是一个概念,不能混淆。缓存里存的数据不一定是持久化的(也可以在内存里缓存),缓存里的数据是用来“提高访问速度的”。
浏览器为了安全起见,会禁止网页的js访问电脑的硬盘(文件系统),浏览器专门提供了特殊的api给网页使用,可以让网页存储一些简单的数据。打开网页时,网页会调用浏览器的api拿自己存储在硬盘上的数据。
浏览器提供的持久化存储方案,有好几种
- Cookie是最经典的一种方案(最老)[主要去讨论这一种]
- LocalStorage是比较新的一种
- indexDB是最新的一种
可以看出我当前浏览器存储了很多Cookie
Cookie是按照域名维度来组织的,不同的域名下有不同的Cookie。一个网站的http请求可能是来自于多个域名的。
www.sogou.com是搜狗的域名,sogou.com也是搜狗的域名
每个Cookie都是一个键值对,Cookie和query string一样,都是程序员自定义控制的
Cookie这里的键值对都是简单的字符串,使用Cookie作为保存数据的手段,只能存一些简单的键值对信息,简单的字符串。存图片,视频什么的是做不到的。
如:可以使用Cookie存上次访问页面的时间,当前网页的访问次数,当前访问页面的身份信息(身份标识,id)
Cookie从哪里来&Cookie到哪里去
从哪里来➷
Cookie是存在浏览器上的,来源是服务器。
例如上面的Cookie,就是浏览器访问了百度服务器之后,由服务器返回的。
在服务器的响应报文中,可以在header中包含一个或多个Set-Cookie这样的资源。
到哪里去➷
Cookie来源于服务器,存储于浏览器,最后还要返回服务器。
当浏览器保存了Cookie之后,下次浏览器访问同一个网站时,就会把之前存储在本地的Cookie再通过http请求的header给带过去。
为啥数据(Cookie)要转一圈
服务器要给很多个客户端提供服务,这些不同的客户端应该要有不同的数据,所以把数据(Cookie)发给客户端他们自己,下次他们再访问时带上数据(Cookie),服务器就知道客户端的相关情况了。
Cookie的典型应用场景(不是唯一应用场景)
最常用的场景就是在客户端维持登录状态
在某个网站登录成功后,浏览器就会记住当前登录用户的身份信息,然后接下来访问网站的其他页面,服务器也能知道是谁在登录。
这个过程和去医院看病很类似
到了医院先挂号,首次挂号时需要提供身份证,同时得到一张就诊卡,就诊卡里存的就是一个身份标识(IC卡里存了个字符串,作为身份标识),每个患者的这个身份标识都不同。此时在医院的系统中,就会创建出一个键值对,键就是我的就诊卡的身份标识,值就是我的病例信息。这个办理就诊卡的流程,就相当于访问网站进行登录。
有了就诊卡之后,后续去各个诊室检查,开药等操作都不需要出示身份证了,只要凭就诊卡就可以知道患者的身份。
就诊卡不需要了就可以注销,此时患者的身份和就诊卡的关联就销毁了(相当于清除Cookie)
又来看病,可以办一张新的就诊卡(相当于得到了一个新的Cookie)
3.认识请求正文
正文中的内容格式和header中的Content-Type密切相关。
HTTP响应详解
1.认识“状态码”
状态码表示访问一个页面的结果(访问成功了,还是失败了,失败的原因是什么)
以下是常见的状态码
200 OK
这是一个最常见的状态码,表示访问成功
404 Not Found
如果你想访问的资源服务器上没有,就会返回出现404,一般是因为请求的路径写错了。
很多网站的404也是精心修饰过的
403 Forbidden
表示访问被拒绝(没有权限)
有的页面需要登录后才能访问,如果未登录直接访问就会出现403。
500 Internal Server Error
服务器内部出现错误,一般是服务器内部代码执行过程中出异常了。
常用的网站一般见不到这种情况
504 Gateway Timeout
访问超时了
当服务器负载比较大的时候,服务器处理单挑请求的时候消耗的时间就会很长,就可以导致出现504,这种情况在双十一等“秒杀”场景中可能会见到,平时不怎么能见到
302 Move temporarily
临时重定向,访问一个旧的URL时会自动转移到新的URL上。
相当于呼叫转移,比如我本来的手机号是123-456-789,后来换了个新号码456-789-555,并且办理了呼叫转移功能,那么其他人拨打123-456-789,就会自动转移到456-789-555这个号码上。
302比较常见,如服务器地址迁移,或者搜索引擎中的点击跳转
状态码小结
4开头是客户端的问题,5开头是服务器端的问题
2.认识响应“报头”
响应报头的基本格式和请求报头的格式基本一致。
类似于Content-Type,Content-Length等属性的含义也和请求中的含义基本一致。
响应中Content-Type的常见取值有以下几种
- text/html:body的数据格式是HTML
- text/css:body的数据格式是css
- application/javascript:body的数据格式是JavaScript
- application/json:body的数据格式是JSON
请求中Content-Type表示客户端给服务器端发送的内容的格式
响应中Content-Type表示服务器端给客户端发送的内容的格式
3.认识响应正文
和请求正文一样,正文的具体格式取决于header中的Content-Type
如何构造HTTP请求
1.浏览器自己构造
- 地址栏里输入URL,可以构造出HTTP请求
- 点击a标签,可以构造HTTP请求
- img、link、script也可以构造HTTP请求
2.通过form表单构造
form表单的重要参数
- action:构造的HTTP请求的URL是什么
- method:构造的HTTP请求的方法是GET还是POST(form只支持GET和POST)
input的重要参数
- type:表示输入框的类型。text表示文本、passw表示密码、submit表示提交按钮
- name:表示构造出的HTTP请求的query string的key、query string的value就是输入框中用户输入的内容
- value:input标签的值,对于type为submit类型来说,value就对应了按钮上显示的文本
如果是方法是get,下图里的键值对会放在URL的query string中,如果方法是post,键值对就会放在body中
form代码和HTTP请求之间的对应关系
- form的action属性对应HTTP请求的URL
- form的method属性对应HTTP请求的方法
- form的name属性对应query string的key
- input的内容对应query string的value
3.通过ajax构造HTTP请求
ajax可以构造各种方法的HTTP请求
form构造的请求一定会触发页面跳转,ajax构造的请求默认不会页面跳转,可以手动控制
页面跳转不是一件好事情,开销大,时间慢,用户得等,通过ajax可以实现“局部刷新的效果”。
ajax全称Asynchronous(异步) Javascript And Xml,之前在多线程里学过synchronized(同步的)
同步这个词,有多种含义(彼此之间没有关系),多线程里所说的同步,指的是“互斥”
在网络通信/IO操作的时候,也涉及到同步,表示的含义是“谁发起的请求,谁负责接收结果“
异步和网络通信中的同步是相对的,表示的是发起请求的主体,不负责去接收结果,而是由别人主动推送过来。
我去餐馆里吃饭,我自己点单,等餐馆老板把饭做好后我自己去端,这就是同步(谁发起的请求,谁负责接收结果)
我去餐馆里吃饭,我自己点单,饭做好后,我不自己去端,而是由服务员端过来,这就是异步
ajax api属于浏览器原生自带的,但是不好用,因此使用jquery代替原生api
https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js
直接运行当前ajax函数,浏览器会报错,这就涉及到了跨域问题
跨域:一个页面在域名a之下,尝试通过ajax访问域名b里的资源,这种情况浏览器是默认禁止的,除非b网站返回的响应明确告诉浏览器说可以跨域访问,市面上大部分网站都是不允许跨域访问的。
上述的都是基于前端(围绕浏览器)来构造的HTTP请求,除此之外,还可以使用postman来构造HTTP请求
HTTPS
HTTPS也是一个应用层协议,是在HTTP协议的基础上引入了一个加密层。
HTTP协议内容都是按照文本的方式明文传输的,这就导致传输过程中出现一些被篡改的情况。如“运营商劫持”。
引入概念
明文:想要传输的最原始的信息
密文:原始的信息经过加密得到的信息
密钥:可以将明文转换为密文,也可以将密文转换为明文的工具
1.对称加密
对称加密就是通过同一个“密钥”,把明文加密成密文,并且也能把密文解密成明文。加密和解密使用同一个密钥。
一个最简单的对称加密方式就是按位异或
我们知道
1.) 0 ^ 0 = 0 , 0 ^ 1 = 1, 0异或任何数=任何数。
2.)1 ^ 0 = 1 , 1 ^ 1 = 0 , 1异或任何数=任何数取反。
3.)任何数异或自己 = 把自己置0。可以推出a^b^b=a ==> 也就是 明文^密钥=密文,密文^密钥=明文
引入对称加密后,即使黑客截获了数据,由于不知道密钥是啥,也无法进行解密
但是有个问题,一个服务器要给很多个客户端提供服务,每个人的密钥必须是不同的(如果相同,黑客一旦知道了一个用户的密钥,所有用户都危险了),典型的做法就是每个客户端在和服务器建立连接之初,先约定一个密钥,然后在进行通信的时候,先把这个密钥发给服务器。
这个有个很大的问题,就是密钥也需要进行加密传输,加密密钥的密钥也需要进行加密,这就无限套娃了,因此密钥的传输使用对称加密就走不通了。就这需要引入非对称加密了
2.非对称加密
非对称加密要用到两个密钥,一个叫做"公钥",一个叫做“私钥”。
由服务器生成一对非对称密钥
一个是公开的出来的,任何人都可以获得的“公钥”
另一个是私密的,只有服务器自己知道的“私钥”
假设客户端已经持有了公钥,然后使用公钥进行加密,由于外界只有公钥,私钥只有服务器有(黑客拿不到),只有服务器可以使用私钥对上述的请求进行解密
![](https://i-blog.csdnimg.cn/blog_migrate/e9b1dcd79a9e807509fe6dd37fa152eb.png)
非对称加密更安全,为什么还要使用对称加密?
因为非对称加密的运算速度非常慢,比对称加密要慢很多。所以非对称加密只对密钥进行加密,其他信息则使用对称加密。
针对上述情况,黑客还是有办法的,就是“中间人攻击”,黑客自己也生成一对非对称密钥,使客户端和服务器在不知不觉中被窃取了信息。
那么接下来问题又来了
- 客户端如何获取到公钥?
- 客户端如何确定这个公钥不是黑客伪造的?
为了解决“中间人攻击”这一问题,引入了证书。
3.引入证书
在客户端和服务器刚一建立的时候,服务器给客户端返回一个证书,这个证书包含了刚才的公钥,也包含了网站的身份信息。证书由第三方公证机构颁发。
这个证书可以理解成是一个结构化的字符串,里面包含了以下信息:
- 证书发布机构
- 证书有效期
- 公钥
- 证书所有者
- 签名
- ......
客户端获取到证书后,会对证书进行校验(防止证书是伪造的),证书自身有一系列的校验机制,
黑客想要篡改证书,很容易被发现。
![](https://i-blog.csdnimg.cn/blog_migrate/62fdd4a9b553c2352c582dad9a6cb090.png)