HTTP 请求报文结构详解:请求行、请求头、请求体各自的作用

HTTP 请求报文结构详解:请求行、请求头、请求体各自的作用

一、请求报文的整体结构

一个 HTTP 请求报文由四部分组成:

复制代码
请求行
请求头
空行
请求体

用文本表示:

复制代码
POST /api/user HTTP/1.1              ← 请求行
Host: localhost:8080                 ← 请求头开始
Content-Type: application/json
Content-Length: 42
Authorization: Bearer xxx
                                     ← 空行
{"username":"jack","age":25}         ← 请求体

空行是请求头和请求体的分隔符,没有请求体时,空行之后直接结束。

三部分不是随意拆分的,而是按"做什么、怎么描述、带什么数据"三层职责划分。


二、请求行:说明"对谁做什么"

1. 结构

复制代码
GET /api/user/1?status=active HTTP/1.1
部分 示例 作用
请求方法 GET 表达操作意图:查、建、改、删
请求目标 /api/user/1?status=active 表达操作对象:路径 + 查询参数
协议版本 HTTP/1.1 表达通信规则

2. 作用

请求行是服务器的第一层路由依据。

服务器拿到请求后,第一件事就是读请求行:

  • 用什么方法?决定交给哪个处理器。
  • 请求哪个路径?决定匹配哪个接口。
  • 什么协议版本?决定用哪套解析规则。

Spring MVC 的 DispatcherServlet 也是先解析请求行,再通过 HandlerMapping 按 方法 + 路径 查找对应的 Controller 方法。

3. 为什么方法放在请求行

方法不是数据,是动作。它和路径一起构成"操作意图",必须在服务器读取任何数据之前就确定。

如果把方法放在请求体里,服务器就必须先读完整个请求体才能知道要做什么,这对路由、鉴权、限流都不利。


三、请求头:说明"这个请求有什么特征和约束"

1. 结构

请求头是一组键值对,每行一个:

复制代码
Host: localhost:8080
Content-Type: application/json
Content-Length: 42
Accept: application/json
Authorization: Bearer xxx
User-Agent: Apipost/1.0
Cookie: sessionId=abc123

2. 作用

请求头负责描述请求的元信息,常见类别:

类别 请求头 作用
目标信息 Host 请求哪个主机
内容描述 Content-Type、Content-Length 请求体是什么格式、有多长
内容协商 Accept、Accept-Language 客户端希望收到什么格式、什么语言
身份认证 Authorization、Cookie 是谁在请求
客户端信息 User-Agent 用什么客户端
缓存控制 If-None-Match、Cache-Control 缓存策略
链路追踪 X-Request-Id、Traceparent 请求链路标识

3. 为什么需要请求头

请求头解决的是"在不读请求体的情况下,就能知道请求的关键属性"。

举例:

  • 服务器看到 Content-Type: application/json,才知道该用 Jackson 解析请求体。如果这个信息放在请求体里,服务器必须先解析才能知道怎么解析,形成死循环。
  • 服务器看到 Authorization,可以先做鉴权,不通过直接返回 401,不需要读取请求体,节省资源。
  • 代理服务器看到 Host,可以决定把请求转发到哪台后端机器,不需要理解请求体内容。

请求头是分层处理的基础:网关、代理、缓存、鉴权组件都只读请求头,不动请求体。


四、请求体:携带"真正的业务数据"

1. 结构

请求体的格式由 Content-Type 决定,常见三种:

表单:

复制代码
Content-Type: application/x-www-form-urlencoded

username=jack&password=123456

JSON:

复制代码
Content-Type: application/json

{"username":"jack","password":"123456"}

文件:

复制代码
Content-Type: multipart/form-data; boundary=----Boundary

------Boundary
Content-Disposition: form-data; name="file"; filename="a.txt"
Content-Type: text/plain

文件内容
------Boundary--

2. 作用

请求体携带客户端提交的业务数据:

  • 创建资源时的字段值。
  • 更新资源时的修改内容。
  • 上传的文件内容。
  • 批量操作的 ID 列表。

3. 为什么请求体单独存在

请求体可能很大,也可能为空。

  • GET 请求通常没有请求体,数据在 URL 查询参数里。
  • POST 上传文件时,请求体可能是几十 MB。
  • 服务器不应该在路由和鉴权阶段就读取请求体,否则会浪费带宽和内存。

把业务数据隔离在请求体里,服务器可以先做路由、鉴权、限流,再决定是否读取请求体。


五、为什么分成三部分

1. 职责分离

部分 回答的问题 谁关心
请求行 对谁做什么 路由、DispatcherServlet
请求头 这个请求有什么特征和约束 鉴权、内容协商、缓存、代理
请求体 具体带什么数据 业务处理方法

每一层只关心自己需要的信息,不需要理解其他层的内容。

2. 分层处理

一个请求可能经过多个组件:

复制代码
客户端 → 网关 → 代理 → 鉴权服务 → 业务服务
  • 网关只读请求行和 Host,做路由。
  • 代理只读请求头,做转发。
  • 鉴权服务只读 Authorization,做身份校验。
  • 业务服务最后才读请求体,处理业务。

如果所有信息混在一起,每个组件都必须解析全部内容,效率低且耦合高。

3. 大小与性能

请求头通常很小,可以被缓存、压缩、快速解析。请求体可能很大,不应该在不需要的时候读取。

分开之后,服务器可以先基于请求行和请求头做出决策,决定是否继续读取请求体。

4. 协议扩展性

请求头是键值对,可以随时新增自定义头,比如 X-Request-Id、X-Client-Version,不需要改变协议结构。

请求体格式由 Content-Type 决定,可以自由切换 JSON、XML、表单、二进制,不需要改变请求行和请求头。


六、和 Spring MVC 注解的对应关系

请求部分 内容 Spring MVC 获取方式
请求行 方法 @GetMapping、@PostMapping、HttpMethod
请求行 路径变量 @PathVariable
请求行 查询参数 @RequestParam
请求头 普通头 @RequestHeader
请求头 Cookie @CookieValue
请求体 JSON @RequestBody
请求体 表单 @RequestParam 或对象绑定
请求体 文件 MultipartFile
全部 方法 + URL + 头 + 体 RequestEntity<T>

这张表能解释很多"为什么":

  • 为什么 @PathVariable 和 @RequestParam 不需要读请求体?因为它们在请求行里。
  • 为什么 @RequestHeader 能拿到鉴权信息?因为请求头在请求体之前。
  • 为什么 @RequestBody 只能读一次?因为请求体是流,读完就没了。

七、记忆方法

1. 寄快递类比

HTTP 请求 快递类比 作用
请求行 快递单上的"寄到哪、什么方式" 路由到正确的地方
请求头 面单上的"易碎、保价、寄件人" 描述包裹特征和约束
请求体 包裹里真正的物品 业务数据

快递员先看快递单决定送哪,再看面单决定怎么处理,最后才拆包裹。服务器处理请求也是这个顺序。

2. 三句话口诀

复制代码
请求行说"对谁做什么",
请求头说"有什么特征和约束",
请求体说"具体带了什么数据"。

3. 处理顺序口诀

复制代码
先看行做路由,
再看头做鉴权,
最后才读体做业务。

4. 为什么分开记

复制代码
行和头很小,可以先读;
体可能很大,按需再读;
分层处理,各管各的。

5. 和注解对应口诀

复制代码
路径参数在行里,@PathVariable;
查询参数在行里,@RequestParam;
认证信息在头里,@RequestHeader;
业务数据在体里,@RequestBody。

八、总结

部分 核心作用 关键特征 Spring MVC 对应
请求行 说明对谁做什么 方法 + 路径 + 协议版本 @GetMapping、@PathVariable、@RequestParam
请求头 说明请求的特征和约束 键值对,体积小,先于请求体 @RequestHeader、@CookieValue
请求体 携带业务数据 格式由 Content-Type 决定,可能很大 @RequestBody、MultipartFile

三部分分开的本质是职责分离和分层处理:请求行负责路由,请求头负责描述和约束,请求体负责承载数据。这样网关、代理、鉴权、业务服务可以各读各的部分,不需要解析全部内容,既提高了效率,也让协议具备扩展性。记住"行定方向、头定条件、体装数据",再记住"先读行、再读头、最后读体"的处理顺序,HTTP 请求结构就不会混乱。

相关推荐
一只小鱼儿吖1 小时前
内网穿透技术全景:原理、安全与实践
网络·安全
bj_bluewei_tech2 小时前
拯救者玩游戏掉帧卡顿,温度不高是什么原因?
网络·电脑·笔记本电脑·玩游戏
pt10432 小时前
Cisco设备网络安全系列-19:网络安全事件驱动与调整
运维·网络
信看2 小时前
【无标题】
网络
Escalating_xu2 小时前
【Linux 网络编程】ARP 的具体过程与 ARP 欺骗:一条 IP → MAC 映射,怎样改变数据的去向?
linux·网络·tcp/ip
坚定的共产主义生产设备永不宕机3 小时前
服务器从裸机开始搭建ESXI虚拟机
linux·服务器·网络
2301_780789663 小时前
智能汽车的“软肋”:车载系统漏洞分析与安全防护框架初探
网络·安全·车载系统·汽车·ddos
Escalating_xu4 小时前
【Linux 网络编程】IP 分片与重组:3000 字节的数据报,怎样拆开又拼回来?
linux·网络·tcp/ip
OpenLoong 开源社区4 小时前
打破 NAT 限制,零侵入跨网络通信:基于 WebRTC 的透明 TCP 隧道跨网络通信方案
网络协议·tcp/ip·开源·webrtc·openloong开源社区