学习HTTP(Hypertext Transfer Protocol)

52 阅读13分钟

超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。


为什么需要HTTP?

前一篇文章我们学习了TCP

TCP很强大,很可靠

但是我们能不能直接用TCP实现客户端和服务端的通信?

当然可以,毕竟TCP是传输层的核心协议,不过,它太底层了,TCP只管传输,不管语义

如果使用TCP,该怎么定义客户端想要什么?如果传输失败了,我怎么知道具体的原因?该怎么定义服务器发回来的是什么类型的资源?

诸如此类涉及更多细节的问题,我们需要一个更上层的协议来规定通信的规则、格式和语义

所以,应用层的HTTP协议就诞生了


使用HTTP解决TCP痛点

HTTP请求行

我们先来解决上面说的第一个问题:怎么定义客户端想要什么网页?

解决这个问题,我们需要定义一个“请求格式”来说明我们的请求意图

HTTP提供了一种解决方案,在客户端使用 请求行(Request Line)

格式如下

Method(请求方法)[空格]URI(统一资源标志)[空格]Version(HTTP版本)[换行]

举个具体例子

GET /about HTTP/1.1

这里的Method就是GET,URI就是/about,Version就是HTTP/1.1

有了这样一个请求行,客户端就能比较明确的表达自己的请求意图了


HTTP响应行

上面还有一个问题,我客户端和服务器在传输过程中如果失败了,仅凭TCP,我客户端根本不知道哪里出了问题。而且我客户端都给你规定了请求行,那你服务端是不是也得来一个响应行。

所以HTTP也有一个在服务端使用的 响应行(Response Line)*

格式如下

Version(HTTP版本)[空格] Status Code(状态码)[空格]Reason(原因)[换行]

举个具体例子

HTTP/1.1 200 OK

这里的Version就是HTTP/1.1,Status Code就是200,Reason就是OK

这样客户端就能比较明确的知道,服务端那边的传输情况了

有了请求行和响应行之后,只能说是比较清晰的进行通信了,但是还缺少一点个性化需求


HTTP请求头(Request Header)和响应头(Response Header)

如果客户端想实现只接收JSON数据格式,服务端想告诉客户端我返回的是HTML

这单纯用请求行和响应行可不够

所以HTTP中设计了一种机制,用来设置“元信息”

请求头和响应头的格式如下

Field Name(字段名)[冒号]Field Value(字段值)[换行]
...
...
Field Name(字段名)[冒号]Field Value(字段值)[换行]

举个例子

请求头
Accept: application/json
表示客户端能接受的数据类型为json
响应头
Content-Type: text/html
charset=utf-8
表示响应内容类型和编码

请求头和响应头的字段非常多,这里不一一列举了


HTTP报文格式

我们已经了解请求行、请求体、响应行、响应体

由它们再加上一点小细节就组成了HTTP报文头部

这里上两张图,来源HTTP灵魂之问,巩固你的 HTTP 知识体系

image.png

image.png

这两张图展示的是HTTP请求头部和响应头部

在它们最下面有一个空行,空行下就相当于是“正文内容了”

所以HTTP和TCP还是很像的,都是header+body的格式


深入HTTP细节

详解请求行中的细节

我们现在知道了请求行中有Method、URI、Version

那Method有哪些、URI是什么、Version又有哪些呢?

Method(请求方法)

先来讲两个最常用的:GET 和 POST

最基础的理解

GET:用于获取资源

POST:用于提交数据

前端视角 GET:常用于获取页面、API 数据(如 /api/usersPOST:常用于表单提交、文件上传、创建资源(如 /api/users 创建新用户)

这两个请求方法经常被放在一起比较,我们又该如何区别他们呢?

最基础的区别就是GET用来获取资源,POST用来提交数据

然后就是它们的传输方式不同

GET的参数附加在URL之后,而POST的参数保存在请求体中

由于GET的参数直接暴露在URL中,所以GET没有POST那么安全

同样的原因GET的参数长度也会被限制,而POST没有限制

然后我们再认识一个名词:幂等性

什么是幂等性?

简单来说就是,多次请求,结果不变

GET是幂等的,而POST是非幂等的

也很好理解,GET只是获取资源,它并没有实际改变服务端的资源,所以理论上,多次请求拿回来的结果不会变化

而POST是提交数据,意味着可能对服务端的资源进行修改,通常会改变服务端的状态,所以理论上不同的请求返回的结果可能也会不同

最后一个就是缓存的区别,我们先记住,后面具体来讲

GET请求行,浏览器会缓存响应,加快下次请求速度

而POST不会,每次请求都对应一个新的响应

除了这两个之外,还有一些不常用的,可以自行查阅学习


URI(Uniform Resource Identifier - 统一资源标识符)

我们先来弄清几个概念

URI包含URL(Uniform Resource Location - 统一资源定位符)和URN(Uniform Resource Name- 统一资源名称)

那我们怎么区分这些概念呢?

URI是很广义的概念,它既可以是URL也可以是URN

但我们现在耳熟能详的是URL,所以我们这里也不做纠结,就来看看URI的URL形态是什么样的

scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]

我们以表格的形式对每个部分进行解释

部分描述示例
scheme协议类型httphttpsftp 等
user:password用户名和密码(已不推荐使用,出于安全考虑)username:password@
host主机地址,可以是域名或 IP 地址www.example.com
port端口号,默认情况下某些协议有默认端口(如 HTTP 默认为 80,HTTPS 默认为 443),可省略:8080
path资源路径,指向服务器上特定的文件或资源/path/to/resource
query查询参数,通常以键值对的形式出现,用 & 分隔?key=value&foo=bar
fragment片段标识符,指向文档内的某个部分(客户端使用,不会发送到服务器)#section1
假设有一个 URL 如下:
https://username:password@www.example.com:8080/path/to/resource?key1=value1&key2=value2#section1

那么拆解如下

  • schemehttps
  • user:passwordusername:password(注意:现代应用中通常不再直接在 URL 中包含用户凭据)
  • hostwww.example.com
  • port8080
  • path/path/to/resource
  • querykey1=value1&key2=value2
  • fragmentsection1

Version-HTTP版本

按照历史,主要有这些版本

  • HTTP/0.9
  • HTTP/1.0
  • HTTP/1.1
  • HTTP/2
  • HTTP/3

我们这里不深入每个版本的细节


详解响应行中的细节

Status Code(状态码)

HTTP 状态码是服务器在响应客户端请求时返回的一个三位数字代码,用于表示请求的处理结果。了解这些状态码对于前端开发人员来说至关重要,因为它们不仅帮助我们理解服务器的响应情况,还能指导我们在不同场景下做出相应的处理。

HTTP 状态码分为五类,每类都有一个共同的百位数字:

类别范围含义
1xx100-199信息性响应:请求已接收,继续处理
2xx200-299成功响应:请求已成功被服务器接收、理解和处理
3xx300-399重定向响应:需要进一步操作以完成请求
4xx400-499客户端错误:请求包含语法错误或无法完成
5xx500-599服务器错误:服务器在处理请求时发生了错误
这么多状态码,我们该怎么记忆?

别急,我们用一个客户+服务员+厨房的模型,来餐厅点个餐吧!

这些常见状态码就是服务员给你的反馈

1xx - “稍等一下”

这类状态码就像是服务员告诉你:“请稍等,我们正在处理您的订单。”

  • 100 Continue : 比如你点的东西比较多,服务员在记录的时候会说:“好的,您继续点吧。”
  • 101 Switching Protocol:比如你原来用菜单点(http),但是服务员想让你用小程序点(https),就会说:“我们换一种方式来处理您的订单”

提示:这类状态码在前端开发中很少直接处理,因为浏览器通常会自动管理它们。

2xx - “一切顺利完成”

当你点的菜很快就上来了,并且完全符合你的预期,这就像是收到了一个 2xx 的响应。

  • 200 OK:最“美丽”的状态码,就像服务员说:“您的菜上好了,请慢用”
  • 201 Create:服务员说:“您的订单创建成功(POST),已经通知厨房去做了”
  • 204 No Content:服务员说:“您的反馈我已经收到了,但是现在给不了您实质性的内容”
3xx - “换个地方试试”

有时候你点的菜可能不在这个厨房A做,需要去另一个厨房B取,这就是 3xx 状态码的作用。

  • 301 Moved Permanently: 服务员说:“您点的这个菜,已经不是厨房A做的了,之后要去厨房B点,请你记住哦”
  • 302 Found: 服务员说:“您点的这个菜,暂时没有放在厨房A,您后续还可以来厨房A点的”
  • 304 Not Modified: 服务员说:“您不是已经点过这个菜了吗,直接拿已经有的就好了(缓存)”

当你访问旧网站链接时,被重定向到新地址:301 Moved Permanently 浏览器使用缓存中的资源而不是重新下载:304 Not Modified

4xx - “出了点问题,可能是你(客户端)的问题”

这类状态码意味着你点餐时可能出现了某些错误,可能是菜单看错了,或者是你点了不该点的东西。

  • 400 Bad Request: 服务员说:“对不起,我听不懂您点的是什么”
  • 401 Unauthorized: 服务员说:“不好意思,这个菜需要先付款/先注册会员才能点”
  • 403 Forbidden: 服务员说:“不好意思,这个菜不是为您准备的,您不能点”
  • 404 Not Found:服务员说:“不好意思,我们这里没有这道菜”
  • 405 Method Not Allowed:服务员说:“您不能用打电话的方式点餐,我们只接受小程序下单。”

提交了一个格式不正确的表单:400 Bad Request 尝试访问一个需要登录才能看到的页面但未登录:401 Unauthorized 访问一个不存在的网页:404 Not Found 不能用 GET 访问一个只接受 POST 的接口)405 Method Not Allowed

5xx - “出了点问题,可能是我们(服务端)的问题”

有时问题出在厨房这边,可能是厨师生病了,或者设备故障了,导致无法完成你的订单。

  • 500 Internal Server Error:服务员说:“抱歉,厨房出了点问题,但是我们也不知道具体发生了什么”
  • 502 Bad Gateway:服务员说:““我想去厨房传话,但走廊被堵住了,消息传不进去。导致我无法完成你的订单”
  • 503 Service Unavailable:服务员说:“订单太多了,我们处理不过来了;或者我们正在停业整顿,导致无法完成你的订单”
  • 504 Gateway Timeout:服务员说:“厨师那边搞太长时间了,可能是煤气灶(Gateway)出问题了,一直只能用小火,我们提前结束了您的订单”

状态码就像是服务器给客户端的‘情绪反馈’:

2xx 是微笑,3xx 是指引,4xx 是摇头,5xx 是抱歉


详解请求头的细节

请求头是客户端(如浏览器)在发送 HTTP 请求时,附加在请求体之前的一组“键值对”,用于向服务器传递额外的信息。 它们不直接参与资源本身的内容传输,但起到了“说明请求背景”的作用,就像你寄快递时填写的“备注信息”。

我们来举几个常见的请求头

  • Host
Host: www.example.com

告诉服务器,请求的主机名和端口

前端无需手动设置,浏览器自动添加

  • User-Agent(用户代理)
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...

告诉服务器客户端的操作系统、浏览器类型、版本等信息

  • Accept
Accept: text/html,application/xhtml+xml,application/json;q=0.9,*/*;q=0.8

告诉服务器,客户端希望优先接收哪种媒体类型(MIME Type)

q=0.9 表示权重(quality factor),值越高越优先

*/*(表示任何类型)

上面的例子就是说

我最想要text/htmlq=0.9) 其次是application/json(q=0.8);实在不行就任何类型
  • Content-Type(通常用于POST)
Content-Type: application/json

告诉服务器,请求体(Body)的数据格式

  • Cookie
Cookie: session=abc123; userId=456

自动携带与当前域名相关的 cookie,用于维持登录状态

  • Authorization
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

携带用户身份凭证,用于认证

上面的记忆方法:我从哪里来?(Host+User-Agent)我想要什么?(Accept)我想发什么?(Content-Type)你怎么知道我?(Cookie+Authorization)


详解响应头的细节

响应头是服务器在返回 HTTP 响应时,附加在响应体之前的一组“键值对”,用于向客户端(如浏览器)传递额外的控制信息。

我们同样的,举几个常见的响应头的例子

  • Content-Type
Content-Type: application/json; charset=utf-8

告诉客户端响应体(Body)的数据类型和编码格式,也就是我(服务器)给你传回来的是什么

  • Content-Length
Content-Length: 1024

告诉响应体的字节长度,也就是我能给你传多少

  • Server
Server: nginx/1.18.0

告诉客户端服务器软件类型和版本

安全建议:生产环境可隐藏或模糊化,避免暴露技术栈

  • Set-Cookie
Set-Cookie: session=abc123;

服务器告诉浏览器保存一个 cookie,用于维持会话状态

  • Cache-Control
Cache-Control: max-age=3600 //缓存 1 小时

控制浏览器和中间代理是否缓存、缓存多久

  • Access-Control-Allow-Origin
Access-Control-Allow-Origin: https://your-app.com

CORS(跨域资源共享)的核心响应头,告诉浏览器“哪些源可以访问我”

同样的记忆方法:我从哪里来?(Server,生产环境慎用)我能发什么,发多少?(Content-Type+Content-Length)你怎么记住我?(Set-Cookie)能让你存东西吗?(Cache-Control)?你能不能来见我?(Access-Control-Allow-Origin)


以上就是与HTTP相关的最基础的知识,这里我们不深入探讨下去,之后有机会,我们还会回来的!

下一篇文章,我们将学习HTTPS,看看它和我们本次介绍的HTTP存在哪些区别


参考资料

HTTP灵魂之问,巩固你的 HTTP 知识体系