HTTP消息格式与报文结构
概念引入
想象你给朋友写一封正式信函:开头写称呼和事由("尊敬的翼王:关于本月服务器维护计划……"),中间分段写正文,结尾署名。HTTP 报文也有类似的"三段式"结构,而且格式更加严格——它就像飞翔公司内部的标准化申请表,每个区域该写什么、写在哪里,都有明确规定。
HTTP 报文(Message) 是客户端与服务器之间传递数据的"信封+信纸",分为请求报文和响应报文两种。理解报文结构,是读懂 HTTP 的第一步。
核心内容
HTTP 报文的三部分结构
无论是请求还是响应,HTTP 报文都由三个部分组成:
┌─────────────────────────────────────┐
│ 起始行(Start Line) │ ← 第一行,说明"要做什么"或"结果如何"
├─────────────────────────────────────┤
│ 首部行(Header Fields) │ ← 多行键值对,说明附加信息
│ Host: www.feixiang.net │
│ Content-Type: text/html │
├─────────────────────────────────────┤
│ (空行,CRLF 分隔) │ ← 一个空行,标记首部结束
├─────────────────────────────────────┤
│ 实体主体(Entity Body) │ ← 实际传输的数据(可选)
│ <html>...</html> │
└─────────────────────────────────────┘
生活比喻:这就像快递包裹——起始行是快递单上的"收发件信息",首部行是包裹上的"易碎品""轻拿轻放"等标签,实体主体则是包裹里的实际物品。
请求报文结构详解
请求行(Request Line) = 方法 + URL + HTTP 版本
GET /api/employees/hangzai HTTP/1.1
Host: www.feixiang.net
User-Agent: Mozilla/5.0
Accept: application/json
| 组成部分 | 示例 | 说明 |
|---|---|---|
| 方法(Method) | GET | 要对资源执行的操作,如获取、提交、删除 |
| URL | /api/employees/hangzai | 请求的资源路径 |
| HTTP 版本 | HTTP/1.1 | 使用的协议版本 |
飞翔公司场景:空少在前端页面点击"查看航仔资料",浏览器发送上述请求到内部 API 服务器,请求路径中的
hangzai就是航仔的工号标识。
响应报文结构详解
状态行(Status Line) = HTTP 版本 + 状态码 + 原因短语
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 256
Date: Mon, 09 Jun 2026 04:00:00 GMT
{
"name": "航仔",
"dept": "技术部",
"role": "后端开发",
"hobby": "钓鱼、看科幻小说"
}
| 组成部分 | 示例 | 说明 |
|---|---|---|
| HTTP 版本 | HTTP/1.1 | 服务器使用的协议版本 |
| 状态码(Status Code) | 200 | 三位数字,表示请求处理结果 |
| 原因短语(Reason Phrase) | OK | 状态码的文字描述,便于人类阅读 |
首部字段分类
HTTP 首部(Header)是报文的"元数据",按用途分为四类:
| 类别 | 作用 | 常见示例 |
|---|---|---|
| 通用首部 | 请求和响应都可能出现 | Date、Connection、Cache-Control |
| 请求首部 | 仅出现在请求中 | Host、User-Agent、Accept、Authorization |
| 响应首部 | 仅出现在响应中 | Server、Location、Set-Cookie |
| 实体首部 | 描述实体主体属性 | Content-Type、Content-Length、Last-Modified |
飞翔公司场景:风速写了一个算法接口,图妹用 Postman 测试时发现返回乱码。检查发现请求缺少
Accept: application/json首部,服务器默认返回了 XML。这就是请求首部的重要性。
空行(CRLF)分隔的重要性
HTTP 报文使用 CRLF(Carriage Return + Line Feed,即 \r\n) 作为每行的结束符。首部行和实体主体之间必须有一个完全空白的行(只有一个 CRLF)。
这个空行是"分水岭"——服务器读到连续两个 CRLF,就知道"首部结束了,后面是正文"。如果漏了这个空行,服务器会把正文误当成首部解析,导致错误。
实体主体的可选性
并非所有 HTTP 报文都有实体主体:
| 场景 | 是否有实体主体 | 示例 |
|---|---|---|
| GET 请求 | 通常没有 | 查询员工信息,参数在 URL 中 |
| POST 请求 | 有 | 提交请假申请,表单数据在主体中 |
| 200 OK 响应 | 有 | 返回航仔的 JSON 资料 |
| 204 No Content | 没有 | 删除草稿成功,无需返回内容 |
| 304 Not Modified | 没有 | 缓存有效,告诉浏览器用本地副本 |
完整示例:飞翔公司内部员工查询
请求报文(空少查询航仔信息):
GET /api/v1/employees/hangzai HTTP/1.1
Host: hr.feixiang.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
响应报文(服务器返回航仔资料):
HTTP/1.1 200 OK
Date: Mon, 09 Jun 2026 04:00:00 GMT
Server: nginx/1.24.0
Content-Type: application/json; charset=utf-8
Content-Length: 312
{
"id": "10001",
"name": "航仔",
"department": "技术部",
"position": "后端开发",
"personality": "沉稳靠谱,话不多但代码干净",
"hobbies": ["钓鱼", "看科幻小说", "深夜撸串"],
"joinYear": 2018
}
注意:HTTP 报文每行以
\r\n(CRLF)结束,首部和主体之间有一个空行(即连续的\r\n\r\n)。上面的示例为可读性去掉了\r,只保留\n换行。实际抓包中\r\n是不可见的控制字符。
本篇小结
- HTTP 报文由三部分组成:起始行、首部行、实体主体
- 请求报文的起始行叫请求行(方法 + URL + 版本)
- 响应报文的起始行叫状态行(版本 + 状态码 + 原因短语)
- 首部字段分四类:通用首部、请求首部、响应首部、实体首部
- 空行(CRLF) 是首部和实体主体的分界线,不可或缺
- 实体主体是可选的,GET 请求和 204/304 响应通常没有主体
- 报文是纯文本格式,便于人类阅读和调试
动手实践
温馨提示: 以下实践示例中涉及的域名(如
www.feixiang.net)、公司场景和接口均为虚构数据,仅用于演示协议原理,实际执行时可能不会产生文档中描述的效果。建议将命令中的域名替换为你自己可访问的真实地址进行练习。
实践:用 curl 查看完整 HTTP 响应
# 显示完整的请求和响应报文(包括首部)
curl -v http://www.feixiang.net 2>&1 | grep -E "(> |< |HTTP)"
# 只显示响应首部
curl -I http://www.feixiang.net
观察响应中的 Server、Content-Type、Date 等首部字段。
实践:用 telnet 手动发送 HTTP 请求
# 连接到服务器(Windows 需先启用 Telnet 客户端)
telnet www.feixiang.net 80
# 然后手动输入以下三行(每行以回车结束),最后多按一次回车:
GET / HTTP/1.1
Host: www.feixiang.net
你会看到服务器返回的原始 HTTP 响应报文。体会"空行分隔"的实际作用。
实践:思考题
- 为什么 HTTP 报文要设计成"首部 + 空行 + 主体"的三段式?如果去掉空行直接拼接会有什么后果?
- 请求报文中的
Host首部有什么作用?如果一台服务器托管了多个网站(虚拟主机),缺少Host会怎样? - 假设波比要提交一个活动报名表单,应该使用 GET 还是 POST?请求报文和响应报文分别长什么样?