网络原理(二):HTTP 请求 - 首行 => 请求方法 & URL & 版本号

目录

[1. http 协议格式](#1. http 协议格式)

[1.1 http 请求](#1.1 http 请求)

[1.2 http 响应](#1.2 http 响应)

[2. http 请求](#2. http 请求)

[2.1 首行](#2.1 首行)

[2.1.1 URL](#2.1.1 URL)

[2.1.1.1 协议名称](#2.1.1.1 协议名称)

[2.1.1.2 服务器 IP 地址 / 域名](#2.1.1.2 服务器 IP 地址 / 域名)

[2.1.1.3 端口号](#2.1.1.3 端口号)

[2.1.1.4 层次结构的路径](#2.1.1.4 层次结构的路径)

[2.1.1.5 查询字符串 Query String](#2.1.1.5 查询字符串 Query String)

[2.1.1.5.1 URL encode](#2.1.1.5.1 URL encode)

[2.1.2 方法](#2.1.2 方法)

[2.1.2.1 GET 方法](#2.1.2.1 GET 方法)

[2.1.2.2 POST 方法](#2.1.2.2 POST 方法)

[2.1.2.3 GET 和 POST 的区别 【经典面试题 ★★★】](#2.1.2.3 GET 和 POST 的区别 【经典面试题 ★★★】)

[2.2 请求头 header](#2.2 请求头 header)


1. http 协议格式

http 协议是一个 "文本格式的协议", 我们可以通过抓包工具, 分析 http 请求/响应的格式.

1.1 http 请求

http 请求由以下四部分组成:

  1. 首行(请求方法 + URL + 版本号)
  2. 请求头(header)(键值对结构)
  3. 空行(用来标识 header 的结束)
  4. 正文(body) (有些请求没有正文(GET), 有些请求有正文(POST))

我们通过 fiddler 抓取 http请求 , 观察其具体结构:

其中, 请求头(header) 是键值对结构, 每一行是一个键值对, 键和值之间使用 冒号+空格 分割. 并且其中的键有哪些取值, 对应的值又有哪些取值, 都是由协议约定的.

1.2 http 响应

http 响应也是由四部分组成:

  1. 首行
  2. 响应头(header)(键值对结构)
  3. 空行 (标识 header 的结束)
  4. 正文(body)

我们通过 fiddler 抓取 http响应 , 观察其具体结构:

响应头(header) 依然是键值对的结构, 但需要注意的是, 响应头中的键值对和请求头中的键值对是有区别的(但是一些重要的键值对可能同时在请求和响应中存在).

2. http 请求

上文说到, http 请求由四个部分组成:

  1. 首行(请求方法 + URL + 版本号)
  2. 请求头(header)(键值对结构)
  3. 空行(用来标识 header 的结束)
  4. 正文(body) (有些请求没有正文(GET), 有些请求有正文(POST))

接下来, 一起聊一聊请求中, 每个部分的详细情况~~

2.1 首行

2.1.1 URL

URL 是首行中的一个重要组成部分.

URL( Uniform Resource Locators, 唯一资源定位符), 描述了网络上唯一资源的位置.

我们在学习 MySQL - jdbc 时, 创建 DataSource 时, 就需要设置 URL, 设置 user, 设置 password.

这里以 jdbc 时, 指定的 URL 为例, 介绍 URL 的组成部分:

可以观察到, URL 通常由以下几个部分组成:

  1. 协议名称
  2. 要访问的服务器的 IP 地址或者域名
  3. 端口号
  4. 带有层次结构的路径
  5. 查询字符串(Query String)
2.1.1.1 协议名称

URL 的开头, 会指定协议名称, 不同的协议, 协议名称是不同的. (这里需要明确, URL 不是 http 专属的概念, 是可以给各种协议提供支持的)

而当我们访问网页时, URL 就遵循 http 的协议进行传输:

2.1.1.2 服务器 IP 地址 / 域名

在 URL 中, 存在要访问的服务器的 IP 地址或者域名. 通过 IP 地址, 来确定要访问服务器的哪一台主机.

注意: **域名和 IP 地址是可以互相转换的.**这个转化的过程是通过 DNS 域名解析系统来完成的(DNS 既是一套服务器系统, 也是一种应用层协议)

有了 IP 地址, 为啥还要有域名呢?? 其实就是方便好记, IP 地址是一连串的数字, 而域名就是一个拼音(字符串的形式).

从某种意义上说, 我们就可以把域名当做为 IP 地址, 把 IP 地址当做域名.

2.1.1.3 端口号

通过 IP 地址可以确定要访问的服务器主机, 而通过 端口号就可以确定访问主机上的哪个应用程序.

但是观察 http 的 URL, 发现, 在 http 的 URL 中, 没有明确指明端口号.

这里需要注意, 没写端口号, 是因为浏览器会自动添加默认的端口号, 而默认添加的端口号是啥, 取决于协议是啥:

  1. http => 80
  2. https => 443

注意: URL 中的 IP 地址(域名) 和 端口号, 都是访问的远端服务器的IP 和端口, 而不是浏览器自身的 "客户端IP和端口".

也就是 五元组 中的 目的 IP 和 目的端口.

2.1.1.4 层次结构的路径

我们想访问的是 服务器中某个主机上某个程序管理的某个资源.

通过 IP 确定了哪台主机, 通过 端口号 确定了哪个应用程序, 而通过这个带有层次结构的路径, 就可以确定这个程序中某个具体的资源.

这个资源可以是一个硬盘上的文件, 也可以是虚拟的资源(通过代码生成的数据).

而为何是带有 "层次结构" 的路径呢?? 我们可以把每一层当做一个目录, 我们是一个目录一个目录的, 一层一层的拨开, 去寻找目标资源.

程序员可以根据需要灵活的自定义层次结构, 对资源进行的分类管理.

2.1.1.5 查询字符串 Query String

URL 中, ? 后面的内容就是查询字符串.

查询字符串对要访问的资源进行补充说明, 也是键值对的结构, 键和值之间通过 = 进行分割, 键值对和键值对间使用 & 进行分割. (键值对的内容都是程序员自定义的)

这里举个例子:

还是以老湿去陕科大六餐厅卖熏肉大饼为例, 老湿在餐厅的 18 号档口盘了一个位置.

不仅卖多种陷的熏肉大饼:

猪肉熏肉大饼, 牛肉熏肉大饼, 鸡肉熏肉大饼.

还卖多种粥:

八宝粥, 皮蛋瘦肉粥, 小米粥.

有一天我去老湿的档口买了一份猪肉的熏肉大饼, 那么向老湿发送的 URL 就如下所示:

查询字符串就是对我们要访问的资源, 做出的进一步的说明.


到这里为止, 上面介绍的 URL 是常见的 URL 的情况, 此外, 官方给出的 URL 的完整版如下:

仔细一看, 其实, 比我们上面说的 URL 多了两个信息:

  1. 登录认证信息
  2. 片段标识符

登录认证信息, 其实现在已经不会写到 URL 里了(单独的隐藏起来了).

片段标识符, 用来区分当前这个页面的哪个部分, 会出现在文档类页面中.(虽然还有, 但是也比较少见了)


2.1.1.5.1 URL encode

URL encode 就是把数据中的二进制数据取出来, 使用 十六进制 来表示, 每个字节前面加上 %

为什么要进行 URL encode 转义呢?? 因为在 URL 中, 存在一些特殊的字符, 如:

  • 用来分割信息 /
  • 用来表示端口号 :
  • 用来表示查询字符串 ?
  • 用来分割查询字符串中的键值对 &

但是, 要知道, query string 中的内容是由程序员自定义的, 万一 query string 中的内容含有以上的特殊字符该怎么办呢??

所以, 就需要对一些数据进行 URL encode 操作, 即转义操作.

转义操作, 不仅仅是对标点符号, 对于中文或者其他非英文系的字符, 也是需要进行转义的. (不能直接把 特殊符号或者非英文字符 放到 URL 中)

对于 encode 和 decode, 都是有一些现成的方法来直接调用完成的.

比如, 前端构造了一个 http 请求, 如果 query string 中涉及到了一些符号或者中文, 就需要调用 JS 的 urlencode 来进行转义.

等服务器收到了这个请求后, 就会调用一个方法来 decode(解转义) 一下.

如果不对 标点符号, 或者非英文字符进行转义, 可能就会使得 URL 解析失败, 进而使得请求失败.

举个例子:

  • c++ 中的 + 是一个字符, 为了防止和 URL 本身的字符发生冲突, 就需要对 + 进行转义, 转义为 16 进制来表示;
  • 若 query string 中出现中文, 同样是需要进行转义的, 转义成该中文字符的 utf8 编码.

2.1.2 方法

介绍完首行中的 URL 后, 接下来来聊一聊首行中的方法.

大家还记得首行中的方法吧:

首行中的 GET 就是方法.

方法 , 通俗来说, 就是描述这次的请求要干啥.

这么多的 http 方法中, 我们只需掌握 GET 和 POST 方法即可, 其中 GET 是最常用的方法. PUT 和 DELETE 方法了解即可.

GET 和 POST 是最常用的两个方法, 从语义上来说 ,GET 从服务器获取资源, POST 是向服务器方法数据, 但是在开发中, 这两个方法并没有严格的区分. (可以将 GET 当做 POST 使用, 也可以将 POST 当做 GET 使用~)

2.1.2.1 GET 方法

GET 方法的典型应用场景如下:

  1. 获取 html, 获取 css, 获取 js

我们在浏览器上打开一个网页, 大多数获取的都是一个 html:

但是如果想要在抓包工具上抓到的 css / js, 就需要对网页进行强制刷新(Ctrl + F5)操作:

为什么强制刷新才能抓到 css / js 呢??

这是因为浏览器的缓存机制. 浏览器为了加快页面的访问速度, 将 css / js / 图片 / 音频 ... 这样的静态资源缓存到了硬盘中. 如果不进行缓存, 那么每次访问都需要从远端的服务器中加载数据, 这样是很慢的. 而经过缓存后, 只需要从本地硬盘中加载数据即可.

而强制刷新操作, 可以忽略本地缓存, 所有资源都会重新从服务器获取.

注意: GET 方法是没有 正文(body) 部分的, 如果需要向服务器发送一些数据, 那么这些数据都会放在 query string 中.

2.1.2.2 POST 方法

POST 方法的典型应用场景如下:

  1. 登录
  2. 上传

对于 POST 请求来说, 请求是带有正文 body 的, 而正文的内容是就是当前上传的数据的内容.

比如, 我进行一次头像的修改, 这就是一个 POST 请求, 而这个请求中 body 的内容就是要上传的新图片.

(body 中既可以填二进制数据, 也可以填文本数据, 并且 二进制数据可以通过 base64 编码转成文本数据)

2.1.2.3 GET 和 POST 的区别 【经典面试题 ★★★】

首先, 先向面试官抛出结论: GET 和 POST 没有本质上的区别!!

其次, 从使用的习惯上来说, 最主要的区别有以下两点:

  1. 语义上的区别: GET 语义上是从服务器获取资源. 而 POST 语义上是向服务器发送资源. (但其实这两个可以混着用)
  2. 携带数据的方式不同: GET 通常是把数据放在查询字符串中(query string). 而 POST 通常是把数据放在正文中(body). (但其实 GET 也可以把数据放在 body中, POST 也可以把数据放在 query string 中).

此外,

  1. GET 请求通常建议设置成幂等的. 而 POST 则无要求. (仅为建议, 不是强制要求, 实际上, GET 不幂等的情况太常见了~)
  2. GET 被设计成幂等了, 那么就允许 GET 请求的结果被缓存. 而 POST 可能是不幂等的, 所以 POST 的结果不能被缓存.

幂等, 是计算机中的一个专业术语, 指请求一定时, 那么得到的响应也是一定的(请求不变, 响应也不会变).

举个例子, 比如奶牛, 第一天它吃的是草, 挤出来的是奶. 那么第二天, 它吃的也是草, 那么它挤出来的肯定还是奶. 这就是幂等. 但是, 如果它吃的是草, 但是第二天挤出来的是 祥 , 那就不是幂等了.


此外, 网上还有一些对 GET 和 POST 不同之处的讨论, 但是这些说法有待商榷:

  • POST 比 GET 更安全

因为有人认为 GET 会把账户名和密码直接放在 query string 中, 就会显示在浏览器的地址栏上, 这是不安全的行为.

但是, 我的理解是, 如果仅仅这样就是不安全的话, 那 POST 也是不安全的呀, 只要我抓个包, 也能看见你 body 里面的内容.

所以, 保证安全的关键是能够做到 "加密传输", 只要是明文传输, 都谈不上安全.

  • GET 的传输数据的长度有限制

其实这就是上古时期的说法了, 以前的 IE 浏览器年代, 对于 URL 的长度是有限制, 所以 query string 中不能放太多的数据, 如果太多就会被截断.

但是, 当前时代, 主流的浏览器对 URL 的长度都已经没有限制了.

  • GET 只能传输文本, 而 POST 可以传输二进制

GET 确实只能传输文本(URL 的 query string 中只能放文本), 但是可以把二进制通过 base64 转换成文本.

此外, GET 也是可以带 body 的(将数据放到 body 中), 只是有些 客户端/服务器 不支持.

除了 GET 和 POST 这两个方法以外, 还有 PUT 和 DELETE 方法 可能会用到.

PUT 和 DELETE 在实现 Restful 风格的 api 的时候会用到(Restful 是定义服务器接口时的一种习惯):

  1. 新增: POST
  2. 删除: DELETE
  3. 修改: PUT
  4. 查询: GET

++END++

相关推荐
AI原吾8 分钟前
探索Python词云库WordCloud的奥秘
开发语言·python·信息可视化·wordcloud
速盾cdn19 分钟前
速盾高防cdn支持移动端独立缓存
开发语言·安全·web安全
LG.YDX27 分钟前
java:拆箱和装箱,缓存池概念简单介绍
java·开发语言
kirito学长-Java27 分钟前
springboot/ssm大学校园生活信息平台Java校园活动论坛交流问卷系统web源码
java·开发语言·spring
编程修仙34 分钟前
I/O流程图
java·开发语言·流程图
翔云API35 分钟前
人脸识别API解锁智能生活、C++人脸识别接口软文
开发语言·数据库·c++·python·ios·php
Ljw...39 分钟前
HTTP(网络)
linux·网络·网络协议·http
FreeLikeTheWind.1 小时前
Qt问题之 “QWidget: Must construct a QApplication before a QWidget“错误
开发语言·qt
dongdongcoding1 小时前
c++中的函数指针
开发语言·c++