详解 HTTP 协议报文格式 & 构造 HTTP 请求

目录

1. HTTP 协议

 1.1 HTTP 协议格式

1.2 HTTP 请求 (Request)

1.2.1 认识 URL

1.2.2 关于 URL encode  

1.2.3 认识 "方法" (method)

1.2.4 常见的报头种类

1.2.5 HTTP 请求中正文的数据格式

1.3 HTTP 响应 (Response)

1.3.1 认识 "状态码"​​​​​​​

1.3.2 认识响应 "报头" (herder)

1.3.3 认识响应 "正文" (body)

1.4 通过 form 表单构造 HTTP 请求

1.4.2 form 发送 POST 请求

1.4.3 通过 ajax 构造 HTTP 请求


1. HTTP 协议

HTTP ( 全称为 " 超文本传输协议 ") 是一种应用非常广泛的 应用层协议. HTTP 往往是基于传输层的 TCP 协议实现的, 目前我们主要使用的还是 HTTP1.1 HTTP2.0 .

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第1张图片

 1.1 HTTP 协议格式

HTTP 协议是服务器与客户端之间通信模式中的一问一答的方式,请求和响应是一一对应的.

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第2张图片

HTTP 是一个文本格式的协议 . 可以使用  Fiddler 抓包 , 分析 HTTP 请求 / 响应的细节.
下图是Fiddler的抓包过程:
详解 HTTP 协议报文格式 & 构造 HTTP 请求_第3张图片

1.左侧窗口显示了所有的 HTTP请求/响应, 可以选中某个请求查看详情.

2.右侧上方显示了 HTTP 请求的报文内容 . ( 切换到 Raw 标签页可以看到详细的数据格式 )
3.右侧下方显示了 HTTP 响应的报文内容 . ( 切换到 Raw 标签页可以看到详细的数据格式 )
4.请求和响应的详细数据 , 可以通过右下角的 View in Notepad 通过记事本打开 .
抓包原理:
详解 HTTP 协议报文格式 & 构造 HTTP 请求_第4张图片

 Fiddler 相当于是一个 "代理" ,在中间传话.如果电脑上还有其他的代理程序,就可能会导致 Fiddler 无法正常工作.

【响应乱码?】

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第5张图片

我们之前学过的 TCP,UDP,IP 这些都属于 "二进制协议", 协议中的数据,是以二进制的方式来组织的,如果拿记事本打开,里面就会出现一堆乱码,而 HTTP 则是 "文本协议", 所以用记事本打开不会出现乱码.

但是为什么返回来的响应也可能出现乱码的现象呢?

因为网络上传输的数据是可以进行压缩的,对于 HTTP 来说, 请求数据数据一般比较简短,就不必压缩,响应数据可能会比较长!! 通过压缩就可以节省传输的带宽.

Fiddler 内置了加压缩功能,如果出现乱码的时候,我们可以点击右侧中间部分,就可以进行解压缩了:

1.2 HTTP 请求 (Request)

HTTP请求分为四个部分,  就拿 Fiddler 抓到的包来分析:

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第6张图片

首行: 请求的第一行

 首行分为三个部分,中间使用空格分割.

请求报头  header

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第7张图片

中间这一段都是请求报头,header 里面都是 "键值对" ,并且这里的键值对都是有固定含义的,每一行都是一个键值对,键和值之间用 冒号,空格 来分割.

空行 - 结束标记

仔细观察 Fiddler 抓到的那张图,可以发现中间有一行是空着的,这是请求报头 (header) 的结束标记. 报头里有多少个键值对 (有多少行? ),是不确定的.遇到空行就认为是结束了.

正文 (body)

 我们可以看到这里是通过 json 来组织的, 格式化之后观察的更清楚:

1.2.1 认识 URL

URL 基本格式
详解 HTTP 协议报文格式 & 构造 HTTP 请求_第8张图片

1.http: 协议名,常见的有 http https, 也有其他的类型. (例如访问 mysql 时用的

jdbc:mysql )
2.登录信息:历史遗留下来的,现在的网站一般都没有这个了
3.服务器地址:此处是一个 " 域名 ", 域名会通过 DNS 系统解析成一个具体的 IP 地址 .
4.端口号:IP 确定主机后,还需要知道地址上对应的应用程序是啥,就可以通过端口号来区分。如果端口号被省略了,就会按照对应的默认值:http://  默认端口 80    https:// 默认端口是443.
5.带层次的文件路径:一个应用程序下,可能管理着很多的资源,一次请求,只能访问一个具体的资源.具体访问哪一个资源,就需要通过带层次的文件路径来明确.
6.查询字符串: 请求发给服务器时带上的一些 "参数" , ? 作为查询字符串的起始标志, ?后面的内容就是查询字符串的本体了,通过键值对的方式进行组织.键值对之间用 & 来分割, 键和值之间使用 = 来分割. 键值对里面的含义,咱们是不清楚的,是由程序员自定义的. (查询字符串,,就给咱们写代码的时候留下了一些自定义的空间,就可以根据需求,来实现一些特定的效果了.)
7.片段标识符:有些 "文档类" 网站有,起到页面内部跳转的效果,定位到一个网页的某个章节.

1.2.2 关于 URL encode  

URL 里面有一些符号,带有特定的含义,如果在 query string 中也出现同样的符号,此时就容易产生误会!! 此时就需要把这些特殊符号进行 "转码", 就叫做 URL encode.

类似的像 %2B%2B   这种符号就是 + 这个 URL encode 之后得到的内容.

这个 URL encode 操作是非常有意义也是有必要的, 如果查询字符串中带有了特殊符号, 就很容易导致浏览器对 URL 解析失败,可能无法跳转到对应的页面.

1.2.3 认识 "方法" (method)

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第9张图片

掌握前两个最重要的: GET, POST

GET

GET 是最常用的 HTTP 方法. 用于获取服务器上的某个资源. 在浏览器中直接输入 URL, 此时浏览器就会发送出一个 GET 请求. 另外, HTML 中的 link, img, script 等标签, 也会触发 GET 请求.

(虽然建议 GET 用于获取资源, 但实际使用的时候,也不一定完全遵守,也可以使用 GET 让服务器新增一个数据/删除一个护具/修改一个数据, 具体还要看代码怎么写的. )

GET 请求的特点:

  • 首行的第一部分为 GET
  • URL query string 可以为空 , 也可以不为空
  • header 部分有若干个键值对结构 .
  • body 部分为空 . 
GET 请求的 URL 长度问题
网上有些说法 : get 请求长度最多为 1024kb,  这样的说法是错误的 .
HTTP 协议  RFC 2616 标准里 , 明确说明 : "Hypertext Transfer Protocol -- HTTP/1.1," does not specify any requirement for URL length.
没有对 URL 的长度有任何的限制 . 实际 URL 的长度取决于浏览器的实现和 HTTP 服务器端的实现 . 在浏览器端 , 不同的浏览器最大长度是不同的, 但是现代浏览器支持的长度一般都很长 ; 在服务器端 , 一般这个长度是可以配置的 .
POST
POST 方法也是一种常见的方法 . 通常 用于提交用户输入的数据给服务器  (登录,上传文件 ).
通过 HTML 中的 form 标签可以构造 POST 请求 , 或者使用 JavaScript ajax 也可以构造 POST 请求 .  (也可以使用 POST 来 获取/删除/修改 数据,并不是绝对的)
POST 请求的特点:
  • 首行的一个部分是 POST
  • URL 里通常没有 query string
  • 通常没有 body
浏览器和服务器交互的时候,总是需要传递一些数据给服务器的。在提交的过程中,相关的信息,可以放到 query string 里,也可以放到 body 中。query string 是固定的键值对格式,body 中则是可以使用更多的格式来组织。
【常见面试题】 GET 和 POST 之间的区别:
GET 和 POST 二者没有本质区别,彼此之间是可以相互替代的,但是使用习惯上还是存在区别的。 主要掌握前两点区别:
1.GET 通常用来获取数据,POST 通常用来给服务器提交数据。(习惯用法)
2.GET 主要通过 query string 来传递数据,POST 则是使用 body 传递数据。(习惯用法)
3.GET 请求一般建议实现成 "幂等的",POST 则不要求 "幂等"。(如果多次输入的内容相同,多次得到的结果也完全相同,称为 "幂等")(习惯用法)
4.GET 一般是可以被缓存的 / 可以放到收藏夹中的,而 POST 一般不要求被缓存 / 不能放入收藏夹。(习惯用法)
为什么会有一些约定俗成的习惯?主要是为了提高代码的可读性 / 可维护性!

1.2.4 常见的报头种类

HOST :  表示服务器主机的地址和端口.

Content-Length :   表示 body 中的数据长度 .
Content-Type:  表示请求的 body 中的数据格式 .

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第10张图片

Content-Length 和 Content-Type 要搭配 body 使用.

我们在学 TCP 的时候, 知道 TCP 是一个面向字节流的协议, 所以务必要明确数据报和数据报之间的边界, 否则就会出现 "粘包问题". 而 HTTP 也是基于 TCP 的, 当 HTTP 报文带有 body 的时候就需要显示的明确出, body 到哪是结束.

明确边界

  • 要么指定长度 (Content-Length)
  • 要么是指定分隔符 (空行...)
User-Agent ( 简称 UA)
表示浏览器 / 操作系统的属性,  描述了你在使用一个啥样的设备上网. 如下图:

  • (Windows NT 10.0; Win64; x64) 表示操作系统的信息.
  • AppleWebKKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 表示浏览器信息.
由于现代的浏览器: chrome, edge, Safari, 已经差距很小了, 所以现在 UA 起到的作用, 主要是区分, 请求是来自于 PC 还是移动端. (操作系统),  但也不是很准.
Referer
表示这个页面是从哪个页面跳转过来的.

 Referer 不一定 100% 有, 主要还是看从哪个页面跳转过来, 如果是直接从浏览器地址栏输入或者直接点击收藏夹, 那么就不会有 Referer.

【Referer 的用处?】

当用户在浏览器 (搜狗) 点击广告的时候, 广告主就得给广告平台付钱 (几块,几十 或者 上百), 相当于是搜狗给广告主的网站引流了 (让更多的人访问到这个网站).

那么搜狗是如何计费的呢?

按照点击次数计费. 当然要根据投入产出比来看 (ROI) , 每次点击让搜狗赚钱, 也得让广告主赚钱,所以故意点击是不生效的, 搜狗会有严格的 "反作弊" 策略.  这个广告被点击了多少次, 搜狗和广告主双方都要计算. 搜狗这里每次点击都会先跳转到搜狗的费服务器; 而广告主的网站也有日志, 网站中哪些请求 referer 是来自于搜狗的.

referer 是否可以被更改? 有些 referer 不是搜狗的, 搜狗是否有办法, 把这些 referer 给改成自己?

在 2015 年之前, 这种情况是存在的, 当然搜狗自己改不了 referer, 但是网络运营商可以做到, 因为你所有的网络访问操作, 都是要经过人家运营商的设备的, 因此网络运营商有能力, 解析出你的请求的内容,并且篡改其中的内容的. (运营商劫持), 网络运营商也有自己的广告平台, 也想给自己增加一些收益, 这就相当于是 "明抢", 那么抢流量算犯法吗?  在当时, 法律上还没有明确的定义. 后来, 搜狗, 百度, 神马这些大厂考虑利益关系, 选择技术上反制, 那就是使用 HTTPS. (虽然打官司能赢,但是赢了官司,输了买卖)

使用 HTTPS 加密的效果: 1.让运营商劫持, 进行破解, 提高了难度. 2. 即使破解了,进行篡改, 客户端浏览器也是容易识别出篡改的结果. 

2015年之后, 这些搜索公司就纷纷升级为 HTTPS 了.

  Cookie
【Cookie 是什么?】
浏览器允许页面在本地持久化存储数据的一种机制.
当我们使用浏览器去访问页面的时候, 一个用户的相关数据信息都是在服务器里存储, 有的时候, 我们希望在浏览器这边也能存下来, 简化一些操作. 最典型的需要在浏览器这里存储的, 就是用户的身份信息, 这样就可以避免反复重复的进行登录.
但是浏览器为了保证用户的安全, 浏览器针对页面上运行的代码, 做出了很多的限制. 最典型的, 就是禁止浏览器页面代码 访问本地磁盘. (如果不限制,后果非常严重, 可能会导致你访问一些网站,不小心就把你本地磁盘清空了)
如果浏览器不让你访问磁盘. 你要想持久化的在浏览器这边保存一些信息,又得访问磁盘, 所以大家各退一步.
  • 浏览器不是完全将你限制死, 允许页面在限定条件下, 能够使用一点点,但是不能访问到磁盘的其他文件.
  • 同时页面代码, 也不能任意存储数, 只能存储简单的键值对.

浏览器查看 Cookie:·        

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第11张图片

浏览器为了进一步的保证安全, 就会将这些 Cookie 分别存放, 每个网站(每个域名)有一组自己的 cookie,彼此之间不干扰. 而且页面代码只能存储和修改 cookie 这样的简单键值对.

【问题】Cookie 的内容从哪来? 到哪去?

从哪来?

Cookie 的内容, 从服务器来, 网页上面主要的数据存储仍然是在服务器上的, 数据也就是从服务器写回给浏览器的

到哪去?

从服务器来的内容再传回给服务器. 例如网站登录之后, 明确了用户的身份信息, 就把身份标识返回给了浏览器. 浏览器下次访问服务器的时候, 就会带着身份标识, 服务器就可以认出用户是谁了.

Cookie 里面的键值对内容是啥含义?

是由程序员自定义的. 只有实现这里功能的程序员才知道.

使用抓包工具演示 Cookie 内容的来去:

【登录前的请求】

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第12张图片

【响应】

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第13张图片

【登录后的请求】

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第14张图片

当我们清空浏览器中的 Cookie 时, 再次登录, 并抓包时, 会发现请求里面是没有 Cookie 的. 此时在响应中可以看到 Set-Cookie, 而 Set-Cookie 的效果 就是服务器给客户端写 Cookie. 当响应来到浏览器这边时, 登录后的请求中的 header 里就出现了 Cookie . 浏览器保存了这些 Cookie 后, 就会在后续的请求中, 带上这个 Cookie 内容.

举例理解上述流程:

假设张三第一次去某医院, 他先要办理一个就诊卡. 办理就诊卡的时候, 就要将身份证给工作人员验证你的身份信息, 此时医院这边就会在医院系统中建立一个患者的档案, 然后给张三一张就诊卡. 就诊卡里保存了当前这个用户的身份标识 (id), 通过 id 就可以在患者的档案中查询出具体的信息, 例如姓名, 年龄, 性别, 以往病史....

上述建档的过程,, 就是浏览器首次访问网站的时候, 网站进行身份验证, 生成对应的信息, 并返回一个身份标识给浏览器的 Cookie 中. 接下来, 张三要去外科, 内科或者影视科看病时, 就只要刷卡, 医生就可以知道他的既往信息了.

除了就诊卡之外, 这些信息都是保存在医院的系统中的, 医院的系统针对张三, 建立的这份档案, 就成为 "会话"  -  session,  session 就相当于银行的保险柜, 里面存了很多钱; 而 Cookie 就相当于一张银行卡, 通过银行卡就可以找到对应的保险柜, 从而把钱取出来.

很多时候, Cookie 和 session 是要搭配使用的, 但也不绝对, 单独的 Cookie 也有用, 不一定保存身份信息, 也可以随意保存程序猿想要保存的信息, 单独的 session 也有用....

1.2.5 HTTP 请求中正文的数据格式

1. json.  
2.urlencoded.  格式就和 query string 是一样的. (键值对, 键和值之间使用 & 分割, 键和值之间使用 = 分割)
3.form-data.   等等.....

结合抓包工具理解....

1.3 HTTP 响应 (Response)

响应也是分为四个部分,结合下图进行分析:

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第15张图片

首行:响应的第一行

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第16张图片

 使用空格对首行分割成三部分

响应报头  header

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第17张图片

同样也是键值对结构.

 空行 - 结束标记

响应报头的结束标记.

正文 (body)

 正文不一定是 JSON 格式,也可以是 CSS ,JS 或者 图片等.

1.3.1 认识 "状态码"​​​​​​​

 响应首行中的 "200 就是状态码, OK 则是状态码描述.

常见的状态码

  • 200 OK
​​​​​​​这是一个最常见的状态码, 表示访问成功.
  • 404 Not Found
​​​​​​​表示没有找到资源.  浏览器输入一个 URL, 目的就是为了访问对方服务器上的一个资源. 
如果这个 URL 标识的资源不存在, 那么就会出现 404
  • 403 Forbidden
​​​​​​​​​​​​​表示访问被拒绝. 有的页面通常需要用户具有一定的权限才能访问(登陆后才能访问). 
如果用户没有登陆直接访问, 就容易见到 403.
  • 405 Method Not Allowed ​​​​​​​
前面我们已经学习了 HTTP 中所支持的方法, 有 GET, POST, PUT, DELETE 等. 
但是对方的服务器不一定都支持所有的方法(或者不允许用户使用一些其他的方法).
  • 500 Internal Server Error
服务器出现内部错误. 一般是服务器的代码执行过程中遇到了一些特殊情况(服务器异常崩溃)
会产生这个状态码.
  • 504 Gateway Timeout
当服务器负载比较大的时候, 服务器处理单条请求的时候消耗的时间就会很长, 
就可能会导致出现超时的情况.
  • 302 Move temporarily
临时重定向. 就相当于手机号码中的 "呼叫转移" 功能. (假如我换号码了,
我去办理一个呼叫转移的业务,其他人拨打我的旧号码时, 就可以自动转移到我的新号码)
在登陆页面中经常会见到 302. 用于实现登陆成功后自动跳转到主页. 
响应报文的 header 部分会包含一个 Location 字段, 表示要跳转到哪个页面.
  • 301 Moved Permanently
永久重定向. 当浏览器收到这种响应时, 后续的请求都会被自动改成新的地址. 
301 也是通过 Location 字段来表示要重定向到的新地址.

状态码分类

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第18张图片

 

1.3.2 认识响应 "报头" (herder)

响应报头的基本格式和请求报头的格式基本一致.

Content-Type

HTTP 响应中 Content-Type 和 HTTP 请求中的 Content-Type 差别还是挺大的.

响应中的 Content-Type 常见取值有以下几种:

  • text/html : body 数据格式是 HTML
  • text/css : body 数据格式是 CSS
  • application/javascript : body 数据格式是 JavaScript
  • application/json : body 数据格式是 JSON

除了以上四种, 还可以是图片等等, 取值种类很多, 主要还是看返回的响应数据是什么.

1.3.3 认识响应 "正文" (body)

例如:application/json

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第19张图片

 其他格式都可以通过抓包工具来查看,  要注意的是, 有的时候写代码要手动指定字符编码, 保证返回的内容, 不是乱码!

1.4 通过 form 表单构造 HTTP 请求

1.4.1 form 发送 GET 请求

当我们运行这个代码, 并输入类容, 按下提交键后, 此时就会构造出 HTTP 请求并发送给服务器.

 使用抓包工具查看:

1.form action 属性对应 HTTP 请求的 URL
2.form method 属性对应 HTTP 请求的方法
3.input name 属性对应 query string key
4.input 的 内容 对应 query string value

1.4.2 form 发送 POST 请求

上述代码只修改 method ,将其改为 POST.

还是原来的页面:

 

使用抓包工具查看:

详解 HTTP 协议报文格式 & 构造 HTTP 请求_第20张图片​​​​​​​ 

主要区别:

1.method GET 变成了 POST
2.数据从 query string 移动到了 body .
form 只支持 GET 和 POST. 其他方法都不支持了!
【问题】提交的数据, 是放到 URL 里比较好, 还是放到 body 里比较好呢?
当使用 GET 的方式提交的时候, 用户名和密码就显示到浏览器地址栏中了, 容易被别人一眼就瞄走了. 所以一般登录都是基于 POST 实现.
那么是不是就认为 POST 比 GET 更安全呢?
这种说法是不成立的!! 保证安全的关键, 是针对密码进行加密. 而不是放到 body 里, 就认为不能直接看到就算安全; 放到 body 中, 如果不加密的话,  虽然浏览器不能直接显示, 但是随便一抓包, 就抓出来了, 也是掩耳盗铃.

1.4.3 通过 ajax 构造 HTTP 请求

ajax 是一个更强大, 更灵活的构造 HTTP 请求的方式. 

ajax 全称 Asynchronous Javascript And XML
浏览器和服务器之间异步交互数据的方式.

此处的 Asynchronous 的 是 "异步" 的意思, 和学习多线程中的 "同步" , "异步" 不是一回事, 此处的 "同步", "异步" 描述的是进行 IO 操作的时候, 结果是发送方自己来主动获取, 还是接收方把结果推送给发送方.

  • 准备 jQuery:

ajax 是浏览器给 JS 提供的一个和服务器交互数据的机制, 浏览器就提供了一组原生的 API - XMLHttpRequest, 但是这个东西用起来非常不方便, 于是很多第三方库, 就针对 ajax 进行了封装, 最典型的就是 jQuery.我们使用 ajax 构造 HTTP 请求时, 在写代码之前, 要先加载 jQuery, 然后才能编写 jQuery 相关的代码. 加载 jQuery 时, 建议把 jQuery 文件内容拷贝到本地.

  • 使用 jQuery 构造请求 (认识 ajax 代码的基本形式)

1. ajax 函数中使用对象作为参数, 这样做的好处是让这些参数是自解释的, 同时参数​​​​​​​顺序都没有限制, 参数有和没有都很灵活.

2.为啥 ajax 叫做 "异步", 就是在上述代码中的回调函数 (success: function(body) {}) 里体现出来的. 向服务器发送请求的程序员, 只管发送, 不必主动去获取结果, 等到结果回来, 浏览器自然会来通知咱们, 然后触发回调函数.

程序运行结果:

会发现控制台里会报错, 这个报错是一个典型的 "跨域" 问题. 当前上述代码的 HTML 对应的 "域名" 是一个本地路径, 而 ajax 访问的请求, 是百度的服务器. 可以理解为 "张家人莫管李家事". 

张家的页面, 想要通过 ajax 访问李家的服务器, 默认是不允许的!! 这个操作就称为 "跨域". 浏览器为了保证网络安全, 禁止 ajax 的跨域访问.

​​​​​​但是跨域操作也不是 100% 禁止的, 只要百度服务器支持, 我就可以跨域访问. 由于我和百度不是 "儿女亲家" 关系, 百度自然不会允许我跨域访问. 后期等我学会自己搭建一个自己的服务器, 再来使用 ajax 演示程序的执行效果.


本篇博客就到这里了, 谢谢观看!!

你可能感兴趣的:(JavaEE初阶,html,http)