欢迎光临
我们一直在努力

【Web基础】HTTP协议详解

目录

一. HTTP是什么

理解HTTP协议的工作过程

二. HTTP 协议格式

抓包工具的使用

Fiddler抓包工具的使用

抓包工具的原理

被抓包的服务器,能否感知到你对其进行了抓包

协议格式

总结

三. HTTP请求(Request)

3.1 认识URL

URL基本格式

URL encode

URL和URI

3.2 认识"方法"(Method)

GET和POST方法的区别(经典面试题)

关于网上一些说法的探究

3.3 认识请求报头(Header)

Host

Content-Length和Content-Type

User-Agent(简称UA)

Referer

Cookie有什么用?

Cookie从哪来?

Cookie怎么存?

Cookie到哪去?

Cookie的具体用途

四. HTTP响应(Response)

4.1 认识状态码

常见HTTP状态码

重定向相关

五. 构造HTTP请求

1. 通过form表单构造

2. 通过Ajax构造

3. 通过编程的方式构造(Java代码)

4. 通过curl命令构造

5. 通过专门的应用程序构造(Postman,Apifox)

六. 总结


HTTP (全称为 "超文本传输协议") 是一种应用非常广泛的 应用层协议.

  • 文本就是字符串,超文本可以理解为能传输二进制数据的字符串.(图片,视频,音频等,都是二进制类型数据)
  • 协议:协议是整个网络世界的基本盘,为了能使传输的内容达到目的地,网络通信的参与方必须遵循相同的规则,对传输的数据格式、含义进行约定

HTTP诞生于1991年,主流版本有HTTP1.0,HTTP1.1,HTTP2.0,HTTP3.0,其中除了HTTP3.0,其余都是基于传输层的TCP协议实现的,HTTP3基于UDP实现。 目前主流使用的版本为HTTP1.1 和 HTTP2.0. 下面主要介绍的是HTTP1.1版本

理解HTTP协议的工作过程

我们平时打开一个网站,就是通过 HTTP 协议来传输数据的.

比如在浏览器输入一个 百度搜索的"网址"(URL)时,浏览器就给百度的服务器发送了一个HTTP请求,百度的服务器就返回了一个HTTP响应.

这个响应结果被浏览器解析之后,就展示成我们看到的内容.(这个过程中浏览器可能会给服务器发送多个 HTTP 请求, 服务器会对应返回多个响应, 这些响应里就包含了页面HTML,CSS,JavaScript, 图片, 字体等信息).

事实上,当我们访问一个网站时,可能涉及不止一次 HTTP 请求/响应 的交互过程 可以通过 chrome 的开发者工具观察到这个详细的过程.

通过 F12 打开 chrome 的开发者工具, 切换到 Network 标签页. 然后刷新页面即可看到如下图效果. 每一条记录都是一次 HTTP 请求/响应

注意:当前大多主流网页,都是通过 https 来进行通信的. https是在http的基础上做了加密/解密的工作,后面会专门来介绍.

二. HTTP 协议格式

HTTP是一个文本格式的协议. 可以通过Chrome 开发者工具或者Fiddler 抓包,分析HTTP请求/响应的细节.

抓包工具的使用

我们这里进行抓包,主要是为了学习HTTP协议格式 关于抓包工具,市面上是有非常多的,非常著名的有wireshark,依赖图形化界面,可以抓各种包,不止HTTP,还有tcp,udp,ip,以太网帧等等,但学习成本较高,这里专注于抓HTTP,此时就可以使用Fiddler工具,使用更简单易上手。

Fiddler下载地址:https://www.telerik.com/fiddler 其中,fiddler everywhere是最新版(收费版),fiddler classic是经典版(免费版)

Fiddler抓包工具的使用

打开fiddler,第一次使用时要简单设置一下:

如果要抓取HTTPS,需要按下面步骤进行设置,这里如果你想抓HTTPS,勾选时就一定要点确定,信任Fiddler根证书(HTTPS特性)

简单介绍下Fiddler的界面

  • 左侧窗口显示了所有的 HTTP请求/响应, 可以选中某个请求查看详情.
  • 右侧上方显示了 HTTP 请求的报文内容. (切换到 Raw 标签页可以看到详细的数据格式)
  • 右侧下方显示了 HTTP 响应的报文内容. (切换到 Raw 标签页可以看到详细的数据格式)
  • 请求和响应的详细数据, 可以通过右下角的 View in Notepad 通过记事本打开.
  • Raw标签页显示的是HTTP原始的数据格式,响应数据如果太长,可能会压缩,看到的就是乱码,可以通过上面记事本打开的方式查看压缩前数据

可以使用ctrl+a全选左侧抓包结果,delete键清除所有被选中的结果

关于fiddler左侧请求显示的颜色,颜色表示了 响应的数据格式

通过蓝色表示HTML,其余可以自行查阅

抓包工具的原理

Fiddler本身相当于一个“代理”。

当浏览器访问baidu.com时,就会把HTTP请求先发给 Fiddler,Fiddler再把请求转发给 baidu 的服务器。当 baidu 服务器返回数据时,Fiddler 拿到返回数据,再把数据交给浏览器。 因此 Fiddler 对于浏览器和 baidu 服务器之间交互的数据细节,都非常清楚。

通过fiddler代理转发,是比较吃硬件资源的(多了一层纯软件对数据的解析过程) 可能fiddler转发消耗的时间比较长,连接对方服务器就超时了 就算没有超时,也会导致打开网页的速度变慢 所以尽量不要没事挂着fiddler

被抓包的服务器,能否感知到你对其进行了抓包

答案自然是不能,抓包是你本地机器上,你自己运行的软件,不是“服务器”

 

这些数据即使你不用fiddler抓包,通过浏览器的开发者工具f12也一样能看到。fiddler只是一层代理,使得你能更方便的看到与服务器交互的数据。 你可以抓包后,对抓包内容进行编程修改,理论上是可以欺骗服务器,但这个本质是程序员没有去做太多的加密,给了"漏洞",一般这种东西,做成了也没什么价值。

协议格式

通过fiddler抓包结果,我们来分析HTTP协议格式

  • HTTP请求格式

HTTP请求的格式主要分为三部分: 请求行,请求头,请求体 首行(请求行):[方法] + [URL] + [版本] Header(请求头):请求的属性, 里面是冒号分割的键值对(key: value); 每组属性之间使用\\n分隔; 遇到空行表示Header部分结束 Body(请求体):这部分可能为空。空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 

  • HTTP响应格式

HTTP响应格式主要也分为三部分:首行,Header,Body

首行: [版本号] + [状态码] + [状态码解释] Header: 请求的属性, 格式为冒号分割的键值对;每组属性之间使用\\n分隔; 遇到空行表示Header部分结束 Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个 Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是 在body中.

为什么HTTP报文中要存在 "空行"?

  • 因为HTTP协议并没有规定协议报头部分的键值对有多少个. 空行就相当于是"报头的结束标记",或者是"报头和正文之间的分隔符".
  • HTTP在传输层依赖TCP协议,TCP是面向字节流的,若没有这个空行,就会出现"粘包问题".
  • 总结

    三. HTTP请求(Request)

    3.1 认识URL

    URL基本格式

    平时我们俗称的 "网址" 其实就是说的 URL (Uniform Resource Locator 统一资源定位符). 互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。URL 的详细规则由 因特网标准RFC1738 进行了约定. 

    URL的标准格式:

    • http:协议方案名,常见的有 http 和 https, 也有其他的类型. (例如访问 mysql 时用的jdbc:mysql )
    • 登录信息:现在的网站进行身份验证一般不再通过URL进行,一般省略
    • 服务器地址:此处是一个域名,域名会通过DNS系统解析成一个具体的IP地址. 通过在cmd执行ping命令可以看到,如ping www.baiducom
    • 端口号:有时候会省略,当省略的时候,浏览器会根据使用协议类型自动决定使用哪个端口。例如上面http协议默认就使用80端口,https协议默认用443,Tomcat默认8080 通过ip决定访问哪个机器,通过端口决定访问机器上的哪个应用程序
    • 带层次的文件路径:一般也叫做资源路径
    • 查询字符串:本质是键值对,键值对之间用&分隔,键和值之间用=分隔
    • 片段标识符:主要用于页面内跳转,通过不同片段标识符跳转至文档的不同位置

    上面内容中,可省略的有:

    • 协议名: 可以省略, 省略后默认为 http:// 或https://,具体看服务器用的哪个
    • ip 地址 / 域名: 在 HTML 中可以省略(比如 img, link, script, a 标签的 src 或者 href 属性). 省略后表示服务器的 ip / 域名与当前 HTML 所属的 ip / 域名一致.
    • 端口号: 可以省略. 省略后如果是 http 协议, 端口号自动设为 80; 如果是 https 协议, 端口号自动设为443.
    • 带层次的文件路径: 可以省略. 省略后相当于 / . 有些服务器会在发现 / 路径的时候自动访问/index.html
    • 查询字符串: 可以省略
    • 片段标识: 可以省略

    URL encode

    • 像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.
    • 比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
    • 一个中文字符由 UTF-8 或者 GBK 这样的编码方式构成, 虽然在 URL 中没有特殊含义, 但是仍然需要进行转义. 否则浏览器可能把 UTF-8/GBK 编码中的某个字节当做 URL 中的特殊符号.

    转义的规则:把要转义的字符,每个字节拿出来,使用十六进制表示,然后从右到左,取4位(不足则直接处理),每2位为一个,前面加上%,编码成%XY格式 如果是中文,则是通过UTF-8编码,直接查询对应码表即可

    如b站主页链接的哔哩哔哩(bilib ,被转为%E5%93%94%E5%93%A9%E5%93%94%E5%93%A9%EF%BC%88bilib

    PS:这里还涉及一个操作叫urldecode,其实就是urlencode的逆过程

    URL和URI

    URL(Uniform Resource Locator)唯一资源定位符 URL(Uniform Resource Identifier)唯一资源标识符

    这两个近似认为是等价的,URL是URI的一种实现方式,虽然URI也有其他的实现方式,但实际大部分情况下,见到的URI就是用URL的方式实现的,其他方式用的很少 比如安卓开发,安卓中获取到资源文件(图标),是通过Android搞得一个URI的规则

    两者不等价更多是体现在URI是一个更广义的概念

    3.2 认识"方法"(Method)

    方法是请求中的一个部分,是一个"动词",表示这个请求要做什么。

    这些方法的含义,都是HTTP设计者的"美好初衷",但实际上,程序员在使用的时候,并没有遵守这样的要求。这些都不是强制要求,而是软性规则 GET本来是获取,但有时候也被用来投递 POST本来是投递,但有时候也被用来获取 有时候根本不用DELETE来删除,而是直接用GET/POST来删除,这些情况都很常见

    就现在而言,天下HTTP共十斗,GET独占八斗,POST一斗,剩下共分一斗。

    在浏览器中直接输入URL,此时浏览器会发送出一个GET请求  HTML 中的 link, img, script 等标签, 也会触发 GET 请求. from表单,postman,apifox,以及任何一个能进行网络编程的语言都可以构造HTTP请求,本质上就是通过TCP socket写入一个符合HTTP协议规则的字符串。

    方法说明
    GET 获取资源
    POST 传输实体主体
    PUT 传输文件
    HEAD 获得报文首部
    DELETE 删除文件
    TRACE 追踪路径
    CONNECT 要求用隧道协议连接代理
    LINK 建立和资源之间的联系
    UNLINE 断开连接关系
    OPTIONS 询问支持的方法

     

     

    我们需要特别关注的,是这些不同类型请求的特点:

    GET请求特点:

    • 请求首行第一部分为GET
    • URL的query string可以为空,也可以不为空
    • header部分有若干个键值对结构
    • body部分一般为空(理论可以不为空,但特别少见)

    PS:如果你听说过get请求长度最多1024kb,这种说法完全是错误的。 HTTP 协议由 RFC 2616 标准定义, 标准原文中明确说明: "Hypertext Transfer Protocol — HTTP/1.1,"does not specify any requirement for URL length. 没有对URL长度做出任何限制 实际 URL 的长度取决于浏览器的实现和 HTTP 服务器端的实现. 在浏览器端, 不同的浏览器最大长度是不同的, 但是现代浏览器支持的长度一般都很长; 在服务器端, 一般这个长度是可以配置的.

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

    POST方法特点:

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

    其他方法

    • PUT 与 POST 相似,通常也把数据放到body中,有幂等建议,一般用于更新 幂等性是指一个操作无论执行多少次,结果都是相同的。核心行为是完全替换目标资源,而不是在原有数据上叠加修改
    • DELETE 删除服务器指定资源
    • OPTIONS 返回服务器所支持的请求方法
    • HEAD 类似于GET,只不过响应体不返回,只返回响应头
    • TRACE 回显服务器端收到的请求,测试的时候会用到这个
    • CONNECT 预留,暂无使用

    GET和POST方法的区别(经典面试题)

    GET和POST其实没有本质区别(即能用GET的地方,也能用POST,反之亦然) 但在使用习惯上,是有一定区别的

  • GET通常用来表示"获取数据"语义,POST表示"提交数据"语义
  • GET通常把给服务器传递的数据放到query string中,POST通常放到body中
  • 关于网上一些说法的探究

    1. GET请求一般实现成"幂等"的,POST请求没有幂等要求(不准确结论) 首先,幂等是数学上的术语,在计算机中的含义是:

    如果一个请求重复产生之后,结果是明确的,就可以认为是幂等的。 即连续发送多次相同请求,得到结果是一定的。(相当于服务器做了缓存)

    出自HTTP RFC2616标准文档,文档中原意是:"建议你把GET实现成幂等的"

    实际上很多场景下,程序员也是并没有遵守的,GET是否幂等,要具体场景具体分析

    比如很多网站,就是要基于大数据,给你做"推荐",此时GET请求的结果必然是不幂等的

    2. GET请求不安全,POST请求安全(这是没有任何依据的错误结论)

    这种说法的依据是,GET实现登录,用户名和密码通常出现在URL的query string中 而POST用户名和密码通常在body中,不会出现在界面上。

    但实际上这种说法完全是错误的。安全与否的关键是加密,而不是是否显示在页面,安全防的是黑客,而不是你身边的人看不看屏幕。这部分主要是看程序员如何处理。

    3. GET请求单次传输的数据量较小,POST请求单次传输的数据量更大(不准确结论)

    这种说法其实是源自以前上古时期,如IE浏览器,当时对URL长度有限制,又因为GET传输的数据一般在URL中,所以有的这种说法 这种说法如果放在20年前(2000年上下),其实完全是正确的,但放到如今,URL的长度早就没有限制了,一个URL长度可以非常长。

    4. 针对GET请求只能传输文本数据,POST可以传输文本和二进制数据(不准确结论)

    URL中确实不能直接放二进制数据,但就现在而言,二进制数据可以通过urlencode / base64转为文本,放到GET请求的URL来传输。所以这种说法也不够准确

    3.3 认识请求报头(Header)

    首先header的整体结构是键值对结构(key: value),多个header之间用\\n分割

    报头的种类非常多,这里仅介绍几个常见的.

    Host

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

    比如Host: gitee.com 现在很多情况下都是只有一个域名,端口号都是用默认的,由浏览器自动填充 比如80,443之类,如果访问的是一个其他端口号的服务器,那么端口号也会在这里. 目的只是为了方便,若从url中获取这些内容,还得进行字符串分割

    通常情况Host里面的内容和URL中ip和端口号部分是一样的,但也有一部分情况不一样,比如使用代理的时候。

    Content-Length和Content-Type

    这两个字段一般同时出现,可能在请求头也可能在响应头.

    Content-Length表示body中的数据长度,单位是字节 Content-Type表示body中的数据格式

    Content-Type常见取值:

  • text/html
  • text/css
  • application/javascript
  • application/x-www-form-urlencode(form表单)
  • application/json(JSON)
  • image/jpg
  • image/png
  • image/gif
  • text/plain(纯文本)
  • multipart/form-data(传输二进制数据,尤其是较大文件)
  • 只有你的HTTP报文中有body,就得有这两个字段 如果没有这两个字段,那么body的内容和格式就全靠对方去猜,有时候浏览器能猜对,但更多的时候还是猜错。

    关于浏览器是如何去猜的:

    比如读body的时候,读者读者碰到了一个GET或POST等文本,此时就意识到下一个请求开始了,这个请求的body结束了

    比如根据你body的格式,猜你的内容是JSON,html,JavaScript等类型

    为什么要专门用一个字段表示body长度?

    这里涉及到TCP的粘包问题,TCP是面向字节流的协议,这意味着发送方给接收方连续发多个应用层数据包时,这些数据包都在接收方的接收缓冲区连成了一片…

    从哪到哪是一个完整的应用层数据包?这个是不好区分的

    为了能够解决这里的粘包问题,一般有三种解决方式:

  • 引入特殊分隔符 HTTP的空行,若没有body,遇到空行就说明请求结束
  • 消息头标识长度 如果有body,使用Content-Length明确标识从哪到哪是一个完整的正文 读完这么多个长度,说明请求结束
  • 固定数据长度 这种方式HTTP并没有使用,其实就是约定传递内容的长度都是固定的。
  • 这里HTTP就同时使用了前面两种方式

    User-Agent(简称UA)

    表示浏览器/操作系统的属性

    示例:

    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36 Edg/149.0.0.0

    UA在早期是个非常有用的字段,前端开发者可以根据这个来检测页面的"兼容性" 但在如今由于各个浏览器差别已经不是很大了,不再需要因为兼容性,而一个网页提供多个版本了所以UA曾经的用途已经不再明显,但随着时代发展,又被赋予了新的用途:

    区分用户设备是 PC 还是 手机,因为PC和手机的页面排版是完全不同的,PC较宽,手机细长条,一般都是不能用同一份的。我们也可以通过一些方式,修改UA,在PC上看到手机上的网页。

    但是需要注意:通过UA来区分手机和PC,这也是曾经流行的做法了,现在更好的方案是前端开发的一个技术:"响应式编程",原理大概是,获取到浏览器窗口的尺寸,根据尺寸,进行不同的排版(CSS3 媒体查询),特点是随着你网页窗口的缩放,页面也会出现响应变化。如CSDN这个平台. 这种方案其实是比通过UA来区分更优秀的,因为通过UA区分,程序员要写两套代码。 不过到现在为止,市面上仍然有非常多的网站,还是通过UA来区分手机和PC端的,只是说未来可能会被"响应式编程"这种技术给取代。

    关于UA的更多内容,可以参考下图

    Referer

    表示这个页面是从哪个页面跳转过来的. 如:Referer: https://www.bilibili.com/ 不是所有的请求都有referer,有跳转,才有referer 直接在地址栏输入 url 访问收藏夹,这也是没有referer的

    Referer 和浏览器的前进,后退功能没任何关系,Referer是发给服务器的,让服务器知道用户是从哪跳转过来的。浏览器的前进后退,是浏览器自身用栈这样的结构,维护你访问了哪些内容,前进后退就相当于出栈入栈的操作。

    这个字段的用途,主要是广告计费。 广告主需要根据广告的点击量,给广告平台付费,广告主和广告平台双方都要进行统计,两边数据对上了,钱才好计算。 其实也不止是广告,很多情况都是需要统计的,如公司做一个商业化产品,就需要收集各种渠道信息等。  

    Cookie的主要用途是浏览器这边,本地存储数据的一种机制。 Cookie中的内容,完全是程序员自定义的。 本地存储:存储到用户的电脑硬盘,如C盘D盘…

    为什么需要Cookie?

    因为一般情况下,网页(浏览器)不能直接访问到你的硬盘的,因为网页中运行的有js代码,如果是恶意网站,里面包含了恶意代码,此时就可以攻击你的硬盘了,十分危险。(PS:安装的应用程序可以访问硬盘) 但有些时候,我们确实需要这个功能(比如用户登录校验): 此时浏览器就提供了几个方案,虽然禁止访问硬盘,但还是开了个口子:

    1. Cookie(经典方案)

    浏览器不允许直接访问硬盘,但允许网页按照 键值对 的格式,组织一个字符串,浏览器会把这个字符串保存到本地硬盘中,后面要用的话,也只能读/改这个字符串,不能进行任何其他操作。

    2.LocalStorage(近几年逐渐崛起的方案),也是本地存储,不多展开

    3. IndexDB(浏览器),也是浏览器给你划分一块指定的空间,与cookie类似

    所以Cookie就是浏览器允许你存储一个字符串到本地,这个字符串是键值对格式的数据。

    Cookie有什么用?

    浏览器给网页提供的本地存储数据的方案(存储数据后,为了后续和服务器交互更方便)

    Cookie从哪来?

    服务器返回:我们在访问一个网站的时候,可能会没有Cookie,但在响应中,服务器会把需要你在本地存储的数据,通过响应头的set-Cookie的方式返回过来,客户端再把这个Cookie存储到本地。

    Cookie怎么存?

    按照字符串(键值对)方式存储。根据域名维度划分.每个域名下有自己的Cookie 且不同域名下的Cookie不会相互影响,比如你访问百度,百度给你一个Cookie,你再访问搜狗,搜狗也给你一个Cookie,这两个Cookie是分开存的,不会放在一起,更不会互相影响到。

    Cookie到哪去?

    本地存储之后,后续访问同一个域名的网站,就会把Cookie的内容通过请求头,传输给服务器。 Cookie就相当于一个"寄存处",服务器有数据想让你存到本地,存了后,再访问服务器的时候,还是要把数据再发给服务器的,否则存的数据岂不是没用了么

    存储的这些内容,都会在往后访问这个服务器的时候,带给服务器,通过请求头的Cookie字段

    这上面就是响应头中,服务器让我们存储在本地的cookie,浏览器会自动进行存储。发的时候也自动进行发送。

    Cookie的具体用途

    如果通用的说,是用来在本地存储数据 如果具体的说,很多场景,实现的很多功能,都是可以基于Cookie方式实现的。

    比如gitee这个网站,我们如果选择主题为亮色,就可以在网页中查看到一个Cookie值为ligth,改成暗色,重启浏览器后,就可以看到网页中有个Cookie值为dark. 这就意味着,如果我们的服务器要实现一个选择切换页面主题功能,那么当前用户选择了哪个主题,我们就可以让这个主题保存在浏览器本地这里,这样后续用户再打开浏览器,就会按照刚才这个主题来设置颜色了(向服务器请求页面时,会带着Cookie过去)

    不光是主题,包括用户上次的访问时间,用户的状态信息等,都是可以通过Cookie来完成的。 前面已经说了,Cookie中的内容,完全是程序员自定义的。(不过有一定大小限制)

    虽说Cookie用途很多,但最核心的,还得是通过Cookie保存用户的登录状态

    上面这就是一种经典的校验用户登录状态的方式,sessionId一般是随机生成的唯一Id,服务器的session里面存储的一般就有用户密码。

    不过Cookie和Session并不是强相关的即使是登录场景,对于服务器来说,也是有很多种方式能够完成用户登录校验的,并不一定非得是Cookie来搭配Session。而对其他场景而言,Cookie也可以完成其他功能,并不局限于登录。

    四. HTTP响应(Response)

    4.1 认识状态码

    状态码位于响应的首行中 状态码表示访问一个页面的结果. (是访问成功, 还是失败, 还是其他的一些情况…). 体现的是服务器的反馈(成功or失败)

    对于状态码,程序员也是可以自定义的,想返回多少就是多少,但是有些状态码有特殊含义,一般会自动返回,且程序员一般也会遵守。

    HTTP状态码分类

    分类描述
    1** 信息,服务器收到请求,需要请求者继续执行操作
    2**

    成功,操作被成功接收并处理

    3** 重定向,需要进一步的操作以完成请求
    4** 客户端错误,请求包含语法错误或无法完成请求
    5** 服务器错误,服务器在处理请求的过程中发生了错误

    常见HTTP状态码

    200 OK 请求成功

    404 NotFound 服务器没有找到你要访问的资源路径

    403 Forbidden 拒绝访问 有的页面需要用户具有一定的权限才能访问,比如登录后才可查看

    405 Method Not Allowed 服务器不支持你所使用的请求方法 如服务器只接收POST请求,而你传了个GET过去

    415 服务器无法处理请求附带的媒体格式 通常是Content-Type导致的问题

    500 Internal Server Error 服务器内部错误 一般自己练习写代码时常见,平时已经很少见了

    504 Gateway Timeout 网关超时 当服务器负载比较大的时候, 服务器处理单条请求的时候消耗的时间就会很长, 就可能会导致出现超时的情况. 一般常见于双十一等"秒杀"活动中

    301 Moved Permanently 永久重定向

    302 Move temporarily 临时重定向

    重定向相关

    重定向:

    典型例子就是你换手机号码了,找运营商办个呼叫转移之类的,此时别人拨打你的旧号码,就会自动转接到新号码上。

    重定向 => 呼叫转移

    访问url1,自动跳转到url2

    临时重定向和永久重定向的区别:

    区别主要在于浏览器是否要做缓存

    302临时重定向:这次访问重定向了,下次就不一定了,这次定到A,下次就可能到B。

    301永久重定向:一直都是重定向到一个固定的值

    所以对于301(永久重定向)这种情况,浏览器是可以做缓存的

    浏览器做了缓存后,后续再访问url1时,此时浏览器就自动知道了你要访问的是url2,就直接拿url2访问了,从而省略了上面两步交互的第一步。

    而对于302,(临时重定向),此时是无法像301这样省略重定向步骤的。

    响应报头和请求报头是一样的,参考上面请求报头即可,不多介绍

    请求正文和响应正文这部分也一样,都是看具体要传什么,根据Content-Type确定传的数据格式,根据Content-Length确定传的数据长度。也不多介绍

    五. 构造HTTP请求

    1. 通过form表单构造

    表单是前端中让用户输入信息的重要途径. 这里说form表单域(除此之外还有表单控件input等)

    form 的重要参数: • action: 构造的 HTTP 请求的 URL 是什么. • method: 构造的 HTTP 请求的 方法 是 GET 还是 POST (form 只支持 GET 和 POST). input 的重要参数: • type: 表示输入框的类型. text 表示文本, password 表示密码, submit 表示提交按钮. • name: 表示构造出的 HTTP 请求的 query string 的 key. query string 的 value 就是输入框的用户输入的内容. • value: input 标签的值. 对于 type 为 submit 类型来说, value 就对应了按钮上显示的文本.

    简单构造一个GET方法

    <form action="demo1.html" method="GET">
    <input type="text" name="userId">
    <input type="text" name="classId">
    <input type="submit" value="提交">
    </form>

    action中url的值可以是相对url和绝对url,绝对url就是一个完整的url,如https://www.baidu.com/demo1.html,相对url就比如上面写的这种,在前后端没有分离的情况下,可以使用。(前端代码和后端代码都在一个项目(文件夹)中)

    此时在页面中点击提交时,就会发送一个GET请求到action填写的URL中,且提交的内容参数是在query string中的,此时构造出的请求请求头为:

    GET http://127.0.0.1:8080/demo01.html HTTP/1.1
    IP:端口号

    要构造post请求,只需要把method参数的GET换成POST即可  

    2. 通过Ajax构造

    Ajax(Asynchronous Javascript And XML)与上面的form不同,ajax构造的HTTP请求是异步的,意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。而如果不适用ajax更新内容,则需要刷新页面.

    这里使用JQuery框架的方式来使用Ajax,具体参数不多介绍,自行了解

    $.ajax({
    type: "get",
    url: "/test/AjaxTest",
    success: function(result) {
    //js代码
    alert("Ajax的GET请求");
    }
    });

    type里面写HTTP请求要用的方法,url与form的action一样,这里用相对url, success表示请求成功情况下(响应状态码为2**),要执行的内容,还有个对应的error,表示请求失败(4**,5**)情况下要执行的内容,写法与上面success类似。 result表示响应体中的body部分 此处构造的请求为:

    GET http://127.0.0.1:8080/test/AjaxTest HTTP/1.1

    这里是可以构造更复杂的请求的,比如用data参数携带一部分数据过去,通过ContentType可以指定携带的数据类型(通常是post请求的做法),也可以直接把参数写在url上(通常是get请求的做法)

    3. 通过编程的方式构造(Java代码)

    只要是能进行网络编程的语言,都可以构造,只不过这里选择采用Java的方式 其他代码也有自己的方式,只演示Java版本

    第一种:比较原始的方案:Java Socket

    package network;

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    import java.nio.charset.StandardCharsets;
    /*
    所谓的 "发送 HTTP 请求", 本质上就是按照 HTTP 的格式往 TCP Socket 中写入一个字符串.
    所谓的 "接受 HTTP 响应", 本质上就是从 TCP Socket 中读取一个字符串, 再按照 HTTP 的格式来解析.
    我们基于 Socket 的知识, 完全可以构造出一个简单的 HTTP 客户端程序, 用来发送各种类型的 HTTP 请求.
    这部分我们是用最原始的方式, 来构造HTTP请求
    */
    public class HttpTestClient {
    private final Socket socket;
    private final String ip;
    private final int port;

    public HttpTestClient(String ip, int port) throws IOException {
    this.ip = ip;
    this.port = port;
    socket = new Socket(ip,port);
    }

    public String get(String url) throws IOException {
    // 构造首行
    String request = "GET " + url + " HTTP/1.1\\n" +
    // 构造header
    "Host: " + ip + ":" + port + "\\n" +
    // 构造空行
    "\\n";
    // 发送数据
    OutputStream outputStream = socket.getOutputStream();
    outputStream.write(request.getBytes());
    // 读取响应数据
    InputStream inputStream = socket.getInputStream();
    byte[] buffer = new byte[1024 * 1024];
    int n = inputStream.read(buffer);
    return new String(buffer, 0, n, StandardCharsets.UTF_8);
    }

    public String post(String url, String body) throws IOException {
    // 构造首行
    String request = "POST " + url + " HTTP/1.1\\n" +
    // 构造header
    "Host: " + ip + ":" + port + "\\n" +
    "Content-Length: " + body.getBytes().length + "\\n" +
    "Content-Type: text/plain\\n" +
    // 构造空行
    "\\n" +
    // 构造body
    body;
    // 发送数据
    OutputStream outputStream = socket.getOutputStream();
    outputStream.write(request.getBytes());
    // 读取响应数据
    InputStream inputStream = socket.getInputStream();
    byte[] buffer = new byte[1024 * 1024];
    int n = inputStream.read(buffer);
    return new String(buffer, 0, n, StandardCharsets.UTF_8);
    }

    public static void main(String[] args) throws IOException {
    // 这里访问的服务器是我自己写的http服务器, 部署在云服务器上.
    HttpTestClient httpTestClient = new HttpTestClient("47.113.228.241", 8080);
    // GET请求
    String getResp = httpTestClient.get("http://47.113.228.241:8080/messagewall.html");
    System.out.println(getResp);
    System.out.println("———————————-");
    // POST请求
    String postResp = httpTestClient.post("http://47.113.228.241:8080/messagewall.html", "this is body");
    System.out.println(postResp);
    }
    }

    第二种:Java的标准库其实还提供了方案,让我们直接访问HTTP服务器或者HTTPS服务器 即HttpClient  =>  Java11支持的

    package network;

    import java.io.IOException;
    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;

    public class MyHttpClient {
    public static void main(String[] args) throws IOException, InterruptedException {
    // 创建一个HttpClient对象
    // 创建实例的时候, 需要使用到工厂方法
    HttpClient client = HttpClient.newHttpClient();

    // 创建一个 HttpRequest, 表示一个 HTTP请求
    // 链式调用, 下面一看就懂了, 需要注意的是这里.build必须放到最后用于收尾, 表示这一串下来构造成一个HttpRequest对象
    HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://www.sogou.com"))
    .GET()
    .header("User-Agent", "xxxxx")
    .build();
    // 发送请求, 获取响应
    // sync-同步 Async-异步
    // 第一个参数是把请求发过去, 第二个参数是得到的响应该如何去处理, 比如这里要以字符串的方式去处理响应
    // send 执行后就会阻塞等待, 直到响应返回回来. 因为涉及到阻塞线程, 所以这里会抛出InterruptedException异常(强制唤醒…)
    HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    // 打印状态码, 响应头, body
    System.out.println(response.statusCode());
    System.out.println(response.headers());
    System.out.println(response.body());
    }
    }

    我们可以根据需要,构造出任意HTTP请求,也可以换成不同的HTTP方法,或在header中添加不同的参数。 如果要构造Post请求,需要修改上面部分内容:

    //1. 把get换成post
    // POST方法和请求体
    // BodyPublishers.ofString()用于将字符串转换为请求体
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))

    4. 通过curl命令构造

    curl是一个功能强大的命令行工具,用于在客户端与服务器之间传输数据 它支持包括HTTP,HTTPS,FTP,SFTP在内的数十种协议 因其轻量,灵活且无需图形化页面的特性,成为了开发者调试API,自动化脚本和进行网络请求的重要工具

    大多数Linux发行版和Windows 10/11都已经预安装了curl,可通过curl –version验证是否安装

    基本语法结构: curl [options] [URL…] options:可选的各种参数,用于控制curl的行为,URL:要访问的一个或多个网址

    示例:

    curl -X POST -d "username=aaa&password=123" https://api.example.com/login

    这里的-X参数是指定HTTP方法,-d是发送POST数据, 除此之外还有-G,将-d数据作为GET参数发送,-H添加请求头等参数 更多参数与用法可以翻阅相关资料,这里不做展开

    5. 通过专门的应用程序构造(Postman,Apifox)

    Postman和Apifox这种软件是专门用来构造HTTP请求的工具软件 可以让我们通过不写任何代码的方式,构造HTTP请求,这部分不多介绍,自行探索即可 最难的一部可能就是如何注册账号了,由于其软件内部都是有非常好的引导的.

    直接浏览器搜索相应软件,注册用户并登录后即可打开软件 可以在左侧直接添加HTTP接口,根据图形化界面中间的内容,进行参数,body,header等设置,设置完后发送请求即可。  

    六. 总结

    HTTP协议是一个非常重要的应用层协议,是从事计算机相关行业工作的人员几乎天天都要打交道的东西,掌握HTTP协议对于理解各种实际遇到的问题,都有很大帮助,特别是通过抓包,状态码等快速定位bug,以及构建HTTP请求,这些可能都是日常工作中经常会用到的内容。 希望本文介绍的内容能够对您有所帮助。

    赞(0)
    未经允许不得转载:171主机测评 » 【Web基础】HTTP协议详解
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址