1. HTTP概述和fiddler的使用
1.1 HTTP是什么
HTTP
全称为 “超文本传输协议”, 是属于应用层最广泛使用的协议之一, 目前主要使用的是HTTP1.1和HTTP2.0, 在本篇中主要介绍的是HTTP1.1
版本, HTTP往往是基于传输层的TCP协议实现的(HTTP1.0, HTTP1.1, HTTP2.0 均为TCP, HTTP3基于UDP实现).
HTTP属于是应用层最广泛的协议之一.浏览器获取到网页,就是基于http.HTTP就是浏览器和服务器之间的交互桥梁.
HTTP 诞生与1991年. 目前已经发展为最主流使用的一种应用层协议.
HTTP 往往是基于传输层的 TCP 协议实现的.我们平时打开一个网站, 就是通过 HTTP 协议来传输数据的.当我们在浏览器中输入一个百度的"网址(URL
)"时,浏览器就会给百度的服务器发送一个HTTP请求,百度的服务器就会返回一个HTTP响应,这个响应结果被浏览器解析之后,把得到的html等数据进行显示出来(渲染) 就得到了我们所看到的网页.这个过程中浏览器可能会给服务器发送多个 HTTP 请求, 服务器会对应返回多个响应, 这些响应里就包含了页面HTML, CSS, JavaScript, 图片, 字体等信息.
1.2 抓包工具fiddler的使用
HTTP协议的交互详细过程,可以借助第三方的工具来看到.称为"抓包"工具.我们所使用的抓包工具是fiddler
.
点击跳转至官网下载fiddler
1.2.1 注意事项
fiddler本质上是一个代理程序,使用的时候有两个注意事项:
- fiddler可能会与别的代理程序冲突,使用的时候要关闭其他的代理程序.(包括一些浏览器插件).
- 要想正确抓包,还需要开启HTTPS功能.HTTPS是基于HTTP的进化版协议.当下互联网上绝大部分服务器都是HTTPS的.fiddler默认不能抓https的包,需要手动启动并安装证书.
Fidder本质上就是一个代理程序, Fidder开启时服务器和客户端之间的传输都需要经过先经过Fidder这里, 即客户端给服务器发送请求会先将请求发送给Fidder, 然后Fidder再把请求发送给服务器; 同样的服务器的响应也需要先经过Fidder, 然后再发送给客户端; 代理分为正向代理和反向代理两种, 代表着客户端的代理, 叫做正向代理, 代表着服务器的代理, 叫做反向代理.
1.2.2 fiddler的使用
在浏览器中输入搜狗的网址进行访问,Fiddler左侧就是捕获到的http/https包 也就是电脑上浏览器使用http和服务器交互的过程,一般来说,蓝色的就说明传输的是一个html页面,绿色的是js,黑色的是数据.这些结果都是浏览器在访问搜狗主页时产生的http请求.浏览器打开一个界面,对应的http请求可以能是一个,也可能是多个.主要关注的是蓝色的包.这个请求是在搜狗主页,其他的请求都是基于这个请求产生的.
双击某个包后, 会在右侧显示详细信息, http是有一定的格式的, Fiddler按照不同的格式解析会呈现出不同的显示效果, 右侧的上下两栏中, 上面是请求, 下面是对应的响应.
点击Raw
, 在这个模式下可以看到http的本体(最原始的效果).
点击view in notepad
可以在记事本中打开,更详细的查看.
我们可以发现在以下响应中有乱码,这是为了节省网络带宽, 有的服务器会对响应数据进行压缩变成了二进制数据(进行了重新编码), 点击下面的黄色按钮就可以解压缩显示正常的响应结果.
点击黄色按钮即可显示:
这里解压缩后得到的文本数据就是搜狗主页html里面的内容, 这些就是Fiddler的基本理解和使用了, 还要补充一下就是并不是所有的数据都可以进行压缩的, 有的数据重新编码之后, 可能体积会更大, 但是像一般的html/js这样的文本文件, 压缩率还是很高的.
2. HTTP协议格式
在上面Fiddler的抓包结果其实可以观察到, http协议其实是个行文本格式
的数据, 相比于tcp这种二进制
格式来说, 更方便用户进行直接观察, HTTP请求本质上就是给tcp socket
里写了一个符合http格式的字符串, 下面就来介绍一下http协议的报文格式, 基本格式如下:
2.1 HTTP请求格式
2.1.1 基本格式
http请求的报文格式由请求行, 请求报头(header), 请求正文(body)这三部分组成, 报头与正文之间使用空行做标记进行分隔.报头与正文之间使用空行做标记进行分隔.
1. 请求行
包含三个部分, 分别是方法, URL, HTTP版本号
, 使用空格来分隔.
方法
用来描述请求的目的是什么, 比如GET
方法一般是用来获取服务器的资源.
URL
是唯一资源定位符, 俗称网址, 用来标识互联网上唯一资源的位置.
最后标明了所使用http协议的版本.
-
请求报头
包含很多行, 由许多的键值对组成, 键和值之间使用:来进行分割, 键值对的数量是不固定的. - 请求正文是可选项, 不一定有.
2.1.2 认识URL
URL
是唯一资源定位符, 与之相似的还有URI
, 这个是唯一资源标识符, 起到身份标识的作用, 和其他资源进行区分, 而实际上, URL也可以起到身份标识的作用, URL可以理解为URI的一种实现, 就像接口与实现类的关系一样, 实际开发中, 这两个也不做区分, 经常会把这两个词混用.
URL的详细规则由因特网标准RFC1738
进行了约定, 且TCP, IP, UDP的协议格式都是RFC系列文档定义的, URL不是HTTP专属的, 很多的协议都可以使用URL.
URL最关键的四个部分是域名/IP(服务器地址), 端口号, 带层次的路径, 查询字符串, URL里的的有些内容不可省略(必选项), 有些内容可以省略, 具体如下:
- 协议方案名: 必选项, 使用http或https等协议方案名获取访问资源时要指定协议类型, 不区分字母大小写, 最后附一个冒号
:
,使用//
与后面的字段分隔, 也可使用 jdbc:mysql:// 或 javascript: // 这类指定数据程序或脚本程序的方案名. - 登录信息: 可选项, 现在基本已经不用了.
- 服务器地址: 必选项, 可以使用DNS可解析的域名或直接使用IP地址来表示, 使用
:
与端口号分隔, 没有端口号则:
省略. - 服务器端口号: 可选项, 描述了要访问主机上的哪一个应用程序, 若该字段为空, 浏览器会提供默认的端口号, http是
80
, https是443
. - 带层次的文件路径: 必选项, 描述访问的服务器上指定位置的资源, 不同的路径, 拿到的资源是不同的, 最简单的路径就是一个
/
, 代表的是http服务器的根目录, 可以理解为http服务器是系统上的一个进程, 就让这个进程管理系统上的一个特定的目录, 这个目录里面的资源都可以让外面进行访问,/
管理的根目录可以是系统上的任意一个目录, 由http服务器具体配置. - 查询字符串: 可选项, 是获取资源的时候带的参数, 是以键值对的方式组织(query string), 以?开头, 键值对之间使用
&
分割, 键和值之间使用=
分割; 表示浏览器或者客户端传给服务器自定义的信息, 对获取的资源提出进一步的要求, 一般是程序员自定义, 所以这部分除非是自己写的, 要不然大概率是看不懂的, 使用#
与片段标识符分隔. - 片段标识符: 可选项, 使用片段标识符通常可标记出已获取资源中的子资源(文档内的 某个位置), 但在RFC中并没有明确规定其使用方法.
在浏览器中输入请求百度主页的网址https://www.baidu.com/
, 会得到如下页面, 这里的文件路径就是/
.
而如果将文件路径换成/default.html
, 得到的就是和上面不同的一个网页了.
在百度搜索框中输入喜羊羊搜索, 得到如下页面.
这里我们在浏览器中看到的URL的查询字符串是有文字显示效果的, 但我们再将这个网址复制下来, 复制到记事本中就不是文字效果了.
这里其实是浏览器/http服务器将查询字符串的值使用url encode
(转义工具)进行了重新编码, 这样做的原因是URL中有些字符是是有特定含义的(比如/, ?
), 这些字符是不能出现在查询字符串中的, 同时这样可以兼容一些不支持中文的字符集, 也方便浏览器和其他一些工具的识别, 如果这里不重新编码, 直接就是中文, 浏览器可能就无法正确识别了; 通常情况下, 一个中文字符由UTF-8或者GBK
这样的编码方式构成, 虽然在 URL 中没有特殊含义, 但是仍然需要进行转义, 否则浏览器可能把UTF-8/GBK编码中的某个字节当做URL中的特殊符号.
2.1.3 方法
请求行中的方法是用来告知服务器请求意图的HTTP方法, 不同的方法描述了不同的语义, 有着不同的意图,通常情况下 比如GET
表示获取资源, POST
表示上传资源, 在实际开发中最常用的也是这两个方法, 其他的方法大部分是用不到的, 但实际开发中, 语义仅供参考, 实际代码可能并不是遵照语义去写的.
各种方法功能如下:
- GET获取资源
GET
是最常用的HTTP方法, 常用于获取服务器上的某个资源, 在浏览器中直接输入URL, 此时浏览器就会发送出一个GET请求, 除此之外,HTML
中的link, img, script
等标签, 也会触发GET请求, 用JavaScript
中的ajax
也能构造GET请求.
GET
的请求报文首行的第一部分为GET, URL的query string
可以为空, 也可以不为空, header
部分有若干个键值对结构, body
部分为空.
- POSE传输实体主体
POST
方法也是一种常见的方法, 多用于提交用户输入的数据给服务器(例如登陆页面跳转的时候会涉及到POST), 通过HTML中的form
标签可以构造POST请求, 或者使用JavaScript
的ajax
也可以构造POST请求.
POST
的请求报文首行的第一部分为POST, URL的query string
一般为空(也可以不为空), header
部分有若干个键值对结构, body部分一般不为空, body内的数据格式通过header中的Content-Type
指定, body的长度由header中的Content-Length指定, 也就是说body中存放的数据内容和格式都是程序员自主定义的.
我们可以使用Fiddler观察POST方法, 比如在码云(Gitee)的登陆页面, 输入用户名, 密码, 验证码之后, 点击登陆, 就可以看到 POST 请求.
- 其他方法
PUT
与POST相似, 只是具有幂等(相同的输入得到的结果也是确定的)特性, 一般用于更新.DELETE
删除服务器指定资源.OPTIONS
返回服务器所支持的请求方法.HEAD
类似于GET, 只不过响应体不返回, 只返回响应头.TRACE
回显服务器端收到的请求, 用于测试.CONNECT
预留, 暂无使用.
面试题:GET和POSE的区别是什么? GET和POST是没有本质区别的, 在大部分场景下彼此之间都可以相互进行替代, GET可以实现POST所具有的特性, 同样POST也可以实现GET所具有的特性, 这两个方法细节上的差别如下: 从语义上来说, GET请求一般用于服务器获取数据, POST请求一般用于给服务器提交数据, 但这并不是强制性要求, 只是建议这样来写. 从习惯用法上说, GET不用有body(请求正文), GET通过query string(查询字符串)来个给服务器传输一些数据; POST有body, POST通过body来传输数据, 并不绝对, 只是使用习惯. 还有, GET请求一般是幂等的, 即相同结果得到的输出结果是相同的, 所以GET是可以被缓存的(把请求的结果保存了下来, 下次请求就不必真的去请求, 直接取缓存结果即可); POST请求一般是不幂等的, 所以POST是不可以缓存的.
关于他们两个的区别, 还有一些说法在现在看来是有些争议的, 比如有说法说GET请求所能传输的数据量存在上限(1KB, 2KB, 1024KB等版本), 这些其实是在早期实现浏览器/服务器的时候弄了这样的限制, 但实际上RFC标准文档中对于HTTP GET请求的长度上限是没有明确规定的, 这个说法放在20年前是正确的, 放在当下就不适用了.
还有说POST比GET更安全的, 得出这个结论的依据是如果使用GET请求进行登录, 此时用户名和密码就通过query string来传递, 就会出现在浏览器中的地址栏中会被别人看到, 但实际上, 安全的核心要素是加密, 安全指的是如果黑客窃取数据, 敏感信息不会泄露, 所以这个说法也是不太靠谱的.
2.2 请求报头关键字段
header
的整体的格式是以 “键值对” 的结构组织的, 每个键值对独占一行, 键和值之间使用:分隔, 这些键值对都HTTP事先定义好的, 具有特定的含义, 这里报头的字段有很多, 这里仅介绍几个常见的关键字段.
- Host 这个字段大概描述了服务器所在的地址和端口.host这里的地址和端口,用来描述最终要访问的目标.
- Content-Length 描述了body中的数据长度.
- Content-Type
描述了描述了
body
中数据的格式, 这里的取值是非常多, 比如值为application/json
说明body中的内容是和应用程序相关的json
格式的数据,application/x-www-form-urlencoded
是from
标签构造的body的数据格式,multipart/form-dataf
也是from表单提交的数据格式(在 form 标签中加上 enctyped=“multipart/form-data”, 通常用于提交图片/文件); 还有text/html, text/css, image/jpg等…Content-Length
和Content-Type
这两个字段, 如果是GET请求, 报文中是没有body的, header中也就没有这两个字段了; 如果是POST请求, 是有body的, 那么这两个字段是必须要有的. - User-Agent(简称UA) 描述了浏览器和操作系统的版本, 之所以有这个字段其实是为了处理和兼容早期版本得浏览器, 在最早期的浏览器上是只支持文本内容的, 浏览器经过之后的发展, 慢慢支持可图片, 音视频, JS等…那么后来的网站开发者写的网页就要考虑网页是否要带这些内容, 索性就针对不同时期的浏览器做了不同版本的网页, 服务器就可以根据User-Agent这个字段来进行判定, 然后给出相应版本的响应, 而现在的User-Agent主要用来区分PC端和移动端.
- Referer 描述了当前页面的来源, 如果直接在浏览器地址栏中输入URL, 或者直接通过收藏夹访问页面时是没有Referer的. 像浏览器中的广告都是都是按点击计费的, 广告主就需要知道用户是通过哪个广告平台跳转到广告主对应的网站, 支付给对应广告平台费用, 但HTTP本身是明文传输的, 很容易有网络运营商劫持的现象出现, 于是就有了HTTPS来解决这个问题.
- Cookie Cookie是header中非常重要的一个属性, 用来管理服务器与客户端之间状态的, 网页默认不允许访问本地计算机的硬盘的, Cookie本质上是浏览器给网页提供的本地存储数据的机制, 对浏览器访问硬盘做出了明确的限制, Cookie中的内容存在于浏览器中(硬盘). Cookie中存储的是一个字符串, 这个字符串中有若干的键值对, 键和值之间使用=分隔, 而键值对之间使用;分隔, 还有一些其他的内容.
这部分内容也是由程序员自定义的, 我们大概率是看不懂的, 只有开发这部分程序代码的程序员才知道,.
关于Cookie的三个问题:
- Cookie是从哪里来的? Cookie中的数据是来自于服务器.服务器会通过HTTP响应的报头部分(Set-Cookie字段)来决定浏览器的Cookie要存什么.
- Cookie是在哪里存的? 可以认为是存在于浏览器中,存在于硬盘的. Cookie在存的时候是按照浏览器 域名的维度来进行细分的, 不同的浏览器有不同的Cookie, 同一个浏览器不同的域名, 对应的也是不同的Cookie, Cookie里面的除了键值对(域名)以外还有过期时间(有效期), 路径等信息, 比如有很多的网站, 登录之后就自动记录了登录状态, 在有效期内访问就不需要重复登录了.
- Cookie要去哪里? Cookie要回到服务器.
客户端同一时刻是有很多的.客户端这里就会通过Cookie来保存当前用户使用中间状态.当客户端访问浏览器的时候,就会自动的把Cookie的内容带入到请求中.服务器就能够知道现在客户端的状态.
2.3 HTTP响应格式
2.3.1 基本格式
HTTP响应的的报文格式由响应行, 响应报头(header), 响应正文(body)这三部分组成, 报头与正文之间使用空行做标记进行分隔.
响应行包含三个部分, 分别是协议版本, 状态码, 状态码描述, 使用空格来分隔.
状态码和状态码描述这两个描述了访问一个页面响应的结果, 是访问成功, 还是失败, 还是其他的一些情况.
比如上图中的200
就是一个很常见的转态码, 表示访问成功, 对应的描述就是OK.
响应报头的基本格式和请求报头的格式基本一致, 类似于Content-Type , Content-Length等属性的含义也和请求中的含义一致.
响应正文的具体格式也是取决于Content-Type.
2.3.2状态码
HTTP状态码负责表示客户端HTTP请求的返回结果, 标记服务器端的处理是否正常, 通知出现的错误等工作; 状态码的职责是当客户端向服务器端发送请求时, 描述返回的请求结果, 借助状态码, 用户可以知道服务器端是正常处理了请求, 还是出现了错误. 状态码的类别如下:
类别 | 原因短语 |
---|---|
1XX Informational (信息性状态码) | 接收的请求正在处理 |
2XX Success (成功状态码) | 请求正常处理完毕 |
3XX Redirection (重定向状态码) | 需要进行附加操作以完成请求 |
4XX Client Error (客户端错误状态码) | 服务器无法处理请求 |
5XX Server Error (服务器错误状态码) | 服务器处理请求出错 |
经常使用的如下:
2XX Success
200 OK ————我正常处理了你的请求。从客户端发来的请求在服务器端被正常处理了。
204 No Content ————请求处理成功,但不给你任何实体信息。在只需要从客户端往服务器发送信息,而对客户端不需要发送新信息内容的情况下使用。浏览器页面也不会更新。
206 Partial Content ————请求处理成功,给你你要的部分。GET 请求,响应中包含由 Content-Range 指定范围的实体内容。
3XX Redirection 3XX 响应结果表明浏览器需要执行某些特殊的处理以正确处理请求. 注意理解"重定向",这个就相当于手机号码中的"呼叫转移".比如我的手机号是1234.后来换个新号码5678,那么不需要让我的朋友知道我的新号码,只需要我办理一个呼叫转移业务,别人拨打1234,就会转移到5678的号码上面. 要注意区别重定向和请求转发, 重定向可定性到外部的资源(跳转到别的网站), 而请求转发只能在服务器内部的资源之间进行转发, 比重定向少了一次交互而更高效; 重定向是HTTP里提供的机制, 而请求转发是servlet/spring里面提供的机制.
301 Moved Permanently ————你要的东西现在在另一个地方,你去那试试。永久性重定向。该状态码表示请求的资源已被分配了新的 URI,以后应使用资源现在所指的 URI。 Location 首部 提示新的 URI 。
302 temporary redirect ——临时性重定向。
303 See Other ——URI 改变,用GET 获取。303 响应状态码返回时,几乎所有的浏览器都会把POST 改成 GET,并删除请求报文内的主体,之后请求会自动再次发送。(301、302也一样变GET,虽然不容许)
304————你不满足条件,不给你内容。该状态码表示客户端发送附带条件的请求 时,服务器端允许请求访问资源,但未满足条件的情况。
307 ————跟302一样,但是,307 会遵照浏览器标准,不会从 POST 变成 GET。
4XX Client Error
400 Bad Request——你发的信息我理解不了,改下重发。该状态码表示请求报文中存在语法错误。当错误发生时,需修改请求的内容后再次发送请求。
401 Unauthorized——无权限访问。第一次失败会要求认证,第二次失败提示无权限或者浏览器不支持该权限功能。
403 Forbidden——对请求资源的访问被服务器拒绝了。——可能未获得文件系统的访问授权,访问权限出现某些问题(从未授权的发送源 IP 地址试图访问)等
404 Not Found——在我这儿找不到你要的东西。该状态码表明服务器上无法找到请求的资也可以在服务器端拒绝请求且不想说明理由时使用。
5XX Server Error
500 Internal Server Error——该状态码表明服务器端在执行请求时发生了错误。也有可能是 Web应用存在的 bug 或某些临时的故障。
503 Service Unavailable ——服务器超负载,维护之类的。
504 Gateway Timeout——该状态码表示网关超时, 可以理解为服务器响应时间太久(长时间未响应), 客户端这边就不等服务器返回响应了, 就会出现这个状态码. gateway是网关的意思, 也就是一个网络的入口/出口, 通常也用来代指一个机房的入口服务器.