网络技术中,最核心的概念,就是"协议",HTTP
就是应用层典型的协议
应用层,很多时候需要程序员自定义应用层协议,也有一些现成的协议,供我们直接使用~
HTTP就是其中的"佼佼者"
- 浏览器和服务器的交互(打开网页)大概率就是HTTP协议
- 手机APP喝服务器之间的交互,也大概率都是HTTP协议
- 服务器之间的相互调用,也可以使用HTTP
最新的版本是HTTP 3.0,但是当前最常见的版本,还是HTTP1.1,以下也主要介绍1.1版本
在浏览器中输入一个"网址",稍等片刻就看到了网页,这个过程中,就是通过HTTP和服务器进行了交互~
HTTP
这个协议,是属于最典型的"一问一答"模型的协议
学习HTTP
协议,主要学习的内容,就是HTTP
报文格式~
报文格式就描述了一个HTTP
请求是啥样的,以及相应是啥样的~
此处需要使用"抓包工具"来捕获请求交互的详细情况~~
“抓包工具"是个特殊的软件,相当于一个"代理程序”,浏览器给服务器发的请求就会经过这个代理程序,进一步就能分析出请求和响应的结果如何~
代理:找个人跑腿~
我和老板进行了哪些交易,这个时候,跑腿(代理)是非常清楚的~
作为程序员,通过代理,就可以知道浏览器和服务器之间具体的交互细节了~
谈到代理,代理,还分两种:
使用抓包工具,来分析HTTP
协议的工作过程~
抓包工具有很多,我使用的是fiddler
(其他的一些,wireshark,Charles,chrome的开发者工具等等…也可以)
简单介绍Fiddler:
左侧区域是抓到的请求 列表
双击左侧某个你想关注的请求详情,就会在右侧窗口显示出请求的具体情况
点击右上角的
raw
标签页,显示HTTP请求最原始的样子点击右下角的
raw
标签页,HTTP响应的最原始的样子由于响应数据,可能体积较大,服务器通常会返回一个"压缩"后的结果
小tips:如果抓不到很多请求的解决办法:
- 需要开启fiddler抓取HTTPS的功能!! 现在互联网上纯HTTP很少了,更多的是HTTPS,HTTPS可以理解成升级版本的HTTP,在HTTP的基础上,加了个加密层
- 如果开启了上述HTTPS也安装了根证书,还是抓不到~ 检查电脑上是否安装了其他的代理程序/代理作用的浏览器插件,fq工具/游戏加速器/steam++,本质上都是代理,这些程序都会和fiddler打架,无法同时运行!!
使用fiddler务必要把其他的代理程序关闭/禁用~
对照着抓包结果看一下请求情况:
GET https://www.sogou.com/ HTTP/1.1
Host: www.sogou.com
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Microsoft Edge";v="107", "Chromium";v="107", "Not=A?Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.42
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://cn.bing.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: SUID=DCD4D38BD352A00A0000000062FB75D6; usid=2EC2C871ED18A00A000000006354C27B; SUV=1667727332968362; browerV=3; osV=1; IPLOC=CN6100; ABTEST=0|1668496465|v17; SNUID=7C6D0FB866638A1B7CD61A0F67F3B218; ld=pyllllllll20t8ezlllllp285VolllllTLkapZllll9lllllRZlll5@@@@@@@@@@; LSTMV=297%2C26; LCLKINT=6081
以上是一个完整的HTTP
请求的样子~
要构造一个HTTP
请求,本质上就是往一个TCP socket
中,按照下列格式来写入数据即可~
HTTP
是一个文本格式的协议
首行:
请求头 header
Host: www.sogou.com
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Microsoft Edge";v="107", "Chromium";v="107", "Not=A?Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.42
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://cn.bing.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: SUID=DCD4D38BD352A00A0000000062FB75D6; usid=2EC2C871ED18A00A000000006354C27B; SUV=1667727332968362; browerV=3; osV=1;
这里是一个按行组织的键值对,每一行,是一个键值对,键和值之间使用 :
空格 来分割
这里的键都是有固定含义的~
空行
一个HTTP
请求的header
可以有若干个,就使用空行作为header
的结束标记,类似于链表的null
正文 body
有的请求有,有的没有,承载一些具体的数据,可能是json格式的,也可能是其他格式的
{"SessionId": "E25091DB353ABD99281B8635D8A845C8","AppId": "Edge_Win32","Language1": "zh-CN","Content": [{"TileId": "en-US","RevisionId": "0","TileElements": [{"LanguageId": "en-US","Text": "3. 空行","TextUnit": 8}]}],"Descriptors":[{"Name": "FlightIds","Value": "wac-wordeditorservicemultiplegrammarcritiquesperse1nce-treatment"},{"Name": "LicenseType","Value": "NoLicense"}]}
完整的HTTP响应的格式~
首行
响应报头 header
同样也是键值对的结构~
Server: nginx
Date: Wed, 16 Nov 2022 07:49:53 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Vary: Accept-Encoding
Set-Cookie: black_passportid=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=.sogou.com
Pragma: No-cache
Cache-Control: max-age=0
Expires: Wed, 16 Nov 2022 07:49:53 GMT
UUID: 8bce19ac-2795-40e5-a59e-f59e38db5c66
Content-Length: 14927
接下来介绍HTTP协议中的细节~
URL
: 唯一资源定位符(用这个来找到网络上的资源)
URI
: 唯一资源标识符(用这个来区分一个网络上的资源)
这俩概念非常相似,很多时候,并不会显式区分~
URL 里面是啥样子的,是有"RFC标准文档"来进行描述的~
RFC标准文档: 描述了很多网络中的协议标准,包括IP,TCP,UDP,HTTP …
URL重要的部分:
🎐URL encode / decode :
URL中已经包含了一些特殊含义的付好了,比如 :
/ ? @ ....
万一query string
的value
中,也包含了这些特殊符号会咋样?
很可能会有问题!! (浏览器可能会错误的识别URL
,服务器也可能会错误的解析URL
)
非常类似于编程语言里的变量名不能是"关键字"
URL encode / decode
本质上就是把特殊符号进行转义了,这里转义的范围不仅仅是这些特殊符号,还有汉字~
转义规则:把待转义的字符串,把每个字符的十六进制表示前加上个%
把原始的字符,转成转义后的字符 =>
URL encode
(编码)
把转义后的字符还原成原始的字符 =>URL decode
(解码)
HTTP
中非常重要的部分~
可以把 方法 理解成 -> 你这个请求想干啥
- GET
- POST
熟悉了GET POST 就可以应对90%以上的场景了
最常用的的HTTP请求方法~
URL
,此时就会触发GET
html
里面的link,a,img,script
也会触发GET
请求href/src
都会引入一个外部的资源~GET
请求,来从服务器拿到对应的数据~HTTP
请求的交互~浏览器缓存:
浏览器在加载页面的时候,往往要加载这个页面依赖的很多资源,css,图片,js等…加载这些资源,显然是需要消耗不少时间的~为了提高页面加载效率,浏览器就会对加载过的这些
css
图片,js
进行缓存(保存在你本地的磁盘上)下次再访问同一个网页,之前的css
,图片,js
就不必重新从网络加载,而是直接读硬盘即可~
使用ctrl+f5
强制刷新,就可以让浏览器不走缓存,直接强制从网络上获取资源~~
form
表单 html
里form
标签,可以构造出GET
请求ajax
(后面在"如何构造HTTP请求"中讲)GET https://www.sogou.com/ HTTP/1.1
Host: www.sogou.com
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: "Microsoft Edge";v="107", "Chromium";v="107", "Not=A?Brand";v="24"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.42
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: SUID=DCD4D38BD352A00A0000000062FB75D6; usid=2EC2C871ED18A00A000000006354C27B; SUV=1667727332968362; browerV=3; osV=1; IPLOC=CN6100; ABTEST=0|1668496465|v17; SNUID=7C6D0FB866638A1B7CD61A0F67F3B218; ld=pyllllllll20t8ezlllllp285VolllllTLkapZllll9lllllRZlll5@@@@@@@@@@; LSTMV=297%2C26; LCLKINT=6081
GET
URL
的 query string
可以为空, 也可以不为空.header
部分有若干个键值对结构.body
部分为空querystring
来传过去的产生POST途径
POST请求:
POST https://edu.bitejiuyeke.com/tms/login HTTP/1.1
Host: edu.bitejiuyeke.com
Connection: keep-alive
Content-Length: 117
sec-ch-ua: "Microsoft Edge";v="107", "Chromium";v="107", "Not=A?Brand";v="24"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.42
Access-Control-Allow-Methods: PUT,POST,GET,DELETE,OPTIONS
Content-Type: application/json;charset=UTF-8
Access-Control-Allow-Origin: *
Accept: application/json, text/plain, */*
Access-Control-Allow-Headers: Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild
sec-ch-ua-platform: "Windows"
Origin: https://edu.bitejiuyeke.com
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://edu.bitejiuyeke.com/login
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
{"username":"xxxxxxxxxxx","password":"xxxxxxx","uuid":"6cda4adb156c4f8ebe13263f069aa0e3","status":0}
POST
url
通常是没有query string
的header
,键值对的形式body
这里通常是有的~ (body
的数据格式有很多种)POST
在传递信息给服务器的时候,通常就会把信息放到body
中~GET
和POST
的区别[经典面试题]
第一步,先盖棺定论:GET和POST没有本质区别,使用GET实现的场景基本都可以使用POST代替,使用POST实现的场景,也可以用GET来代替
第二步,再来谈细节上的区别~
GET
的语义,是从"服务器获取个数据",POST
的语义,是"往服务器上提交个数据"(这里是建议你设计的时候采取这样的语义~ )- 使用习惯上,给服务器传递的数据,
GET
通常是放在url
的query string
中,POST
通常是放在body
中(GET能否把数据放在body里? 也是可以的,不过很少见,浏览器不一定能支持,其他的http客户端是可以支持的,POST能否把数据放在query string里? 完全可以~但是也少见,浏览器啥的都是支持的)GET
请求建议实现成"幂等"的!!POST
一般则不要求实现成"幂等"(输入是确定的,输出结果也就是确定的),设置服务器的时候,就需要提供一些"接口/api
",api
传入的参数,就视为是输入,api
返回的结果,就视为是输出,基于GET
的api
一般会建议设置成幂等的,基于POST
的api
则无要求,但也只是建议- 在幂等的基础上,
GET
的请求结果是可以被缓存的,POST
则一般不会缓存(浏览器默认的行为,如果GET
确实是幂等的,就不必处理,就让浏览器缓存,如果当前GET
不是幂等的,就需要通过特殊技巧避免浏览器产生缓存 [典型的技巧就是让每次GET请求的url都不相同,通过特殊的 query string 来保证url不同])