引言

HTTP 请求报文由请求行、请求头部、空行 和 请求包体 4 个部分组成

本片文章将从以下四个方面对HTTP请求报文进行解析

  1. URL
  2. 方法
  3. 请求报头
  4. 正文

认识URL

我们先抓一个包来看一下URL在包里面的位置

平时我们俗称的 “网址” 其实就是说的 URL (Uniform Resource Locator 统一资源定位符).

互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它.

URL的详细规则由因特网标准RFC1738 进行了约定,大家可以点击链接进行查看

一个URL的格式如下:

  • 协议方案名: 常见的有 http 和 https,告诉你是哪个协议 也有其他的类型.(例如访问 mysql 时用的jdbc:mysql )
  • 登陆信息: 现在的网站进行身份认证一般不再通过 URL 进行了, 一般都会省略,属于上古时期的方式,现在都没有人这么进行登录了
  • 服务器地址: 要访问的服务器是哪个,此处是一个 “IP地址/域名”, 域名会通过 DNS 系统解析成一个具体的 IP 地址
  • 端口号: 上面的 URL 中端口号被省略了, 当端口号省略的时候, 浏览器会根据协议类型自动决定使用哪个端口,这个端口是服务器的端口 (例如 http 协议默认使用 80 端口, https 协议默认使用 443 端口)
  • 带层次的文件路径: 描述了要访问服务器的哪个资源,一个服务器可提供很多资源供外界访问,比如,web服务器(网站),就可能会包含很多不同的网页,就可以通过这里的路径区分不同的网页。b站中,点开不同的视频,其对应的文件路径就不同。
  • 查询字符串(query string): 具体的表示了请求的资源是什么,是浏览器给服务器传递的参数,本质是一个键值对结构. 键值对之间使用 & 分隔, 键和值之间使用 = 分隔.比如查询字符串=>"蛋糕",这些键值对的意思我们并不知道,这是人家程序猿自定义的。

  • 片段标识: 区分页面中不同的部分,主要用于页面内的跳转。

上述主要掌握IP,端口号,带层次的路径,查询字符串。

query string

query string 中的内容是键值对结构. 其中的 key 和 value 的取值和个数, 完全都是程序猿自己约定的. 我们可以通过这样的方式来自定制传输我们需要的信息给服务器

关于 URL encode

我们在浏览器搜索c++的时候,地址栏中并不是显示的c++,而是%2B%2B,这就是urlencode。

注意:像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.query string里面的value部分,一旦也包含这些符号不就乱套了吗,就会使url的解析出现问题。比如value部分是一个问号,正常来说我们是解析到问号(包含问号),问号后面的就是查询字符串,结果现在有两个问号,到底是哪个问号后面是查询字符串,这就造成一些困扰。因此为了造成不必要的麻烦,就引入了urlencode,urlencode就是针对value进行转义,这里的转义规则,不只是针对一些标点符号,中文汉字也会触发urlencode。
例如:搜索"你好"

此处看到的不是转义过后的,是因为为了让用户看起来比较直观

如果把这串url复制粘贴到记事本,那么就"原形毕露"了,在网络上传输的url,都是转义之后的,只不过是浏览器为了让用户看起来更加直观,所以就按照转码之前的方式进行显示的,实际上网络传输的是转码之后的

汉字要进行转码的原因:

转码规则:上述就的 "+" 转义成 "%2B"

认识 “方法” (method)

上篇文章呢,谈到首行里面包含了方法,通过方法就描述了请求要干啥。

方法的种类有以下几种:

其中最常用的为GET,POST,其他的很少用到,所以下面着重介绍GET和POST

GET方法

GET 是最常用的 HTTP 方法. 常用于获取服务器上的某个资源.网络上大部分请求都是GET

抓取必应搜索utf8页面的数据:

它的请求数据就是一个GET请求,问号后面需要传递给服务器的数据都是以query string表示的,里面没有包含body,这就是一个GET请求。也只是通过query string告诉服务器要搜索啥,服务器返回整个网页。

GET 请求的特点

  • 首行的第一部分为 GET
  • URL 的 query string 可以为空, 也可以不为空.
  • header 部分有若干个键值对结构.
  • body 部分为空

POST 方法

POST 方法也是一种常见的方法. 多用于提交用户输入的数据给服务器(例如登陆,上传文件).

例如登录某教务系统:这个请求就是POST,可以看到url里面并不包含query string,但是有body

上传文件,比如gette更换头像:url里面不包含参数

body里面就是图片的本体,图片本身是二进制数据,由于http通常是以文本进行传输,这里对图片的二进制数据进行转码了,转成了文本数据。这里的转换不是urlencode,而是base64(文本里面的%不是转码的标志,跟urlencode没有任何关系)。

POST 请求的特点

  • 首行的第一部分为 POST
  • URL 的 query string 一般为空 (也可以不为空)
  • header 部分有若干个键值对结构.
  • body部分一般不为空. body内的数据格式通过 header 中的 Content-Type指定
  • body 的长度由header 中的 Content-Length 指定

总结:在实际编程中,使用这两个方法的时候,也能使用GET提交数据,也可以是POST获取某个数据。但是在使用习惯来说GET通常不会搭配body,有需要传输的数据,通过query string来提交给服务器,POST通常不搭配query string,通过body传输数据,这只是使用习惯上的差别。

上述这些HTTP请求是如何构造的呢?

GET和POST的区别

这是一个经典面试题。网上的说法有各种说法,但是有点问题~~

本质上没有区别 :

  1. 不同的方法只是代表不同的语义, 但不一定严格遵守
  2. 使用 GET 的场景,使用 POST 也可以, 这二者一般是可以相互代替的
  3. GET 也可以有正文, POST 也可以没有正文, 并非绝对

使用习惯上有区别 :

  1. 语义不同: 从语义上来说GET 通常用于获取数据, POST 通常用于提交数据.
  2. 传递数据的时候,GET通常使用query string,POST通常使用body
  3. 服务器对于GET 请求,经常设计成是幂等的,POST 请求设计则不要求幂等(如果多次请求得到的结果一样, 就视为请求是幂等的)。这个要求只是HTTP协议标准文档给出的建议。
  4. GET 请求的结果可以被缓存, 可以被浏览器收藏夹收藏,POST 一般不行。 (这一点也是承接幂等性)。一个网站通过GET获取到一些图片,比如搜狗的主页,搜索栏上面的logo,像这个图片,就是通过浏览器,浏览器通过GET请求从服务器获取到,我们通过抓包,就看不到是哪个请求是获取图片,之所以没有看到就是被缓存了。要想看到,按住crtl在点击刷新。浏览器就可以缓存这些图片,下次访问这个网站就不必从网络获取了,直接从之前缓存的数据读取(缓存在硬盘上的)。

正常获取页面数据:

crtl强制获取:

认识请求 “报头” (header)

header中的键值对跟query string不同,query string中的键值对是程序猿自己定义的,而header中的键值对都是标准规定的内容。

header中包含的键值对有很多,但是大部分都不关键,主要介绍几个关键的。

Host

表示请求对应的主机的ip和端口

在 URL中虽然已经写入了域名, 通常情况下这两个是一致的, 但如果不是直接访问, 而是通过代理访问服务器, 这二者就不同了

Conte-Length

表示 body 中的数据长度,单位是字节

一旦有了body就需要知道body的长度,才能知道一个完整的http请求。

由于HTTP是基于TCP,那么这里就会谈到粘包问题,前面我们谈到的粘包问题有两种解决方案,一个是指定分隔符,一个是指定长度。如果GET请求没有body,通过空行;如果是POST请求,有body,通过空行找到body的开始,通过Conte-Length找到body的介绍位置。

Content-Typent

表示请求的 body 中的数据格式

通过HTTP协议,传输的数据有很多种类,比如图片,视频,音频,字体,html,json,css。这些不同的数量 浏览器/服务器 有不同的处理方式,对于图片要显示出来,对于html要渲染成网页,对于js要执行里面的代码......所以为了告诉浏览器,你要针对这些数据怎么处理,就需要明确约定好数据类型。

  • application/x-www-form-urlencoded: form 表单提交的数据格式. 此时 body 的格式形如:

  • multipart/form-data: form 表单提交的数据格式(在 form 标签中加上enctyped=“multipart/form-data” . 通常用于提交图片/文件. body 格式形如:

  • application/json: 数据为 json 格式. body 格式形如(最重要掌握的方式)

User-Agent (简称 UA)

抓取必应的数据:

UA里的信息主要有两个部分:

  • 1)浏览器版本
  • 2)操作系统版本

描述了用户使用啥样的设备,打开你的网页

UA的用途:

在以前UA是有作用的,对于今天来说作用不是很大了。

使用UA区分不同版本的网页,意味着网站开发者就需要维护两套代码。程序员就发明了一个新技术,"响应式布局"(前端技术),只需要写一套代码,这个代码就能根据你设备的尺寸(宽度),设置不同的样式,从而起到很好的适应各种设备的效果。

比如Vue的官网:

宽屏:

窄屏:

Referer

表示当前页面是从哪个页面跳转过来的.

如果直接在浏览器中输入URL, 或者直接通过收藏夹访问页面时是没有 Referer 的.

在搜狗浏览器搜索不孕不育(页面跳转):此处的Referer就是表示从搜狗主页跳转过来的

用途:对于网页上面的广告,用户点击一次搜狗就能挣钱(按点击计费),那么在一个月之内某个广告被点击的次数就需要进行统计,搜狗需要统计,广告主也需要统计。每次点击就会把请求发送到搜狗服务器上,搜狗的服务器就可以记录一条日志(日志就是打印)。广告主这边也会有服务器,每次收到请求就会记录一条日志,统计日志的数目即可。广告主可能有多渠道投放,就需要对不同的渠道进行区分,此时就可以通过Referer进行区分,是从搜狗跳转过来的Referer就是搜狗的域名,从百度跳转过来的Referer就是百度的域名。

运营商劫持每天都会对互联网公司造成巨大损失,就从技术上进行"反制",HTTPS就是重要的反制手段,HTTPS直接针对HTTP请求中的内容(包括Header进行加密),这里的加密不是说不让运营商看,而是方式运营商能够篡改。(关于https如何加密,后序介绍)

Cookie

Header中最重要的一个,日常开发&面试中高频问题!!!

Cookie抓包

通过Fiddler查看搜狗页面数据的cookie:

这里面也是键值对格式的内容,和query string类似,都是程序员自己定义的,要搜狗的程序员才知道是什么意思!!

Cookie这里的键值对本质上都是能够在客户端的硬盘上持久化保存。

网页是运行上浏览器上的,默认情况下,一个网页不能直接访问用户的硬盘,这样的操作是很危险的(如果打开一个网页,如果网页突然把你的资料删了)。但是有的网站确实需要在客户端这边存储一些必要的信息,希望能够持久化的存储(写到硬盘上,即使重启也还在)。浏览器就给网页提供了特定的机制,就是Cookie机制。

Cookie机制也不是让网页随意访问硬盘,网页对于你的硬盘是没法直接读写的,浏览器对于操作硬盘做了特殊的封装,相当于提供了一个/一组特殊的文件,只能往这个文件中写,并且你写的内容,也必须是键值对(键和值都是文本)。

总的来说,Cookie就是浏览器提供给网页的一种能够持久化存储数据的机制(经典的机制)。

Cookie具体是如何存的呢?

咱们没法直接看到Cookie对应的文件(chrome看不到,edge是可以看的),浏览器会针对不同的网站(或者域名),每个网站都有自己的cookie文件,百度存的cookie文件和搜狗存的cookie文件不会影响的。

通过edge打开哔哩哔哩网站:

查看步骤:点击这把锁

里面有一个cookie:

这三个域名都有对应的cookie:

注意:上述一个网页有多个域名的原因是,一个网页在加载的时候,可能在多个服务器获取数据(比如说在某个服务器获取html,在某个服务器获取图片资源,在某个服务器获取数据......)。

展开对应的域名里面就要cookie文件:

上述这些就是b站上面的cookie,打开其他网站也有对应的cookie。

在本地存储的这些Cookie在清除浏览器历史记录的时候是都可以清除的。

Cookie是从哪里来的?

cookie中的数据都是来自于服务器(服务器返回给浏览器的)。

比如:删除在edge浏览器上打开奇客网站进行删除cookie

删除cookie:

通过Fiddler抓取刷新后的响应数据:这些键值对信息就会保存到浏览器的cookie中

此时的奇客网页中,删除的cookie数据又有了:

其中的一个键值对就和上述通过Fiddler抓取的响应数据中的Set-Cookie里面的键值对是匹配的:

Cookie保存到浏览器之后,后序浏览器访问该网站的时候,就会在请求Header中,把之前保存的这些Cookie键值对都给带入进去,还要再发给服务器。

再次刷新页面抓取请求数据:

Cookie数据本来就来自于服务器,为啥后续还要发回服务器呢?

注意,一个服务器不止给一个客户端提供服务,是要应对很多很多个不同的客户端的!!!

理解登录过程

Cookie虽然很多键值对都是程序员自定义的,但是往往会有一个特殊的键值对,大部分网站都会有的key,用来标识用户的身份信息

比如说你清除网站的cookie之后,大概率就需要重新登录。

例子:

  1. 到了医院先挂号. 挂号时候需要提供身份证, 同时得到了一张 “就诊卡”, 这个就诊卡就相当于患者的 “令牌”.(这个就诊卡只是存储了一个字符串,类似于身份标识,那么医院的系统中就会存储你这个身份标识(id)以及对应的身份详情,包括不限于名字年龄性别......).
  2. 后续去各个科室进行检查, 诊断, 开药等操作, 都不必再出示身份证了, 只要凭就诊卡即可识别出当前患者的详细信息.
  3. 看完病了之后, 只要不是你自己不想要就诊卡了, 就可以注销这个卡. 此时患者的身份和就诊卡的关联就销毁了. (类似于网站的注销操作)
  4. 又来看病, 可以办一张新的就诊卡, 此时就得到了一个新的 “令牌”,之前的信息也都在
  5. 但是有时候可能很久不去,你的卡片使用期限到了,或者卡片升级了。这时候你就需要重新办理就诊卡

上述就诊卡中存储的id就是会话id(sessionid),对应的在服务器上保存的id,以及先关联的各种详细信息,就称为会话(session)。

登录过程:

注意:这里说的登录是指登录一次之后,后续访问网站的其他页面不需要重新登录(这是同一个sessionid),如果是退出之后,再重新登录,这是不同的sessionid。

Cookie总结

  • 1.Cookie是啥?

浏览器本地存储数据的一种机制(不是唯一的一种,只是典型的一种)。

  • 2.Cookie怎么存储?

按照不同的域名,分别存储在硬盘上的,不同域名之间的cookie互不干扰,键值对使用文本存储,键和值之间都是程序员自定义的。

  • 3.Cookie从哪里来?

Cookie (中存储的数据)从服务器来: 第一次登陆一个网站的时候, 服务器会返回一个响应, 在这个响应报文中, 通过 Set-Cookie 把需要存储的值返回给客户端, 客户端进行存储

在该响应报文里面包含了一个 gitee-session-n 这样的属性, 属性值是一串很长的加密之后的信息. 这个信息就是用户当前登陆的身份标识. 也称为 “令牌(token)”

  • 4.Cookie到哪里去?

Cookie 要回到服务器, 当客户端收到 Set-Cookie 中的数据后, 后续每次请求网页的时候, 给服务器发送的请求报文中都有一个 Cookie 字段, 相当于把 Cookie 里面的值再带给服务器

后续请求你中的 Cookie 字段也包含了一个 gitee-session-n 属性, 里面的值和刚才服务器返回的值相同, 后续只要访问 gitee 这个网站, 就会一直带着这个令牌, 直到令牌过期(销毁)/下次重新登陆

  • 5.Cookie有什么用?

Cookie 是一浏览器客户端在本地存储用户自定义数据的一种机制, 比如存储用户的身份信息, 让服务器通过 Cookie 来识别用户身份

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部