JV-02 HTTP详解
02 · HTTP 协议详解(HTTP in Depth)
📅 预计 45 分钟 | ⭐ = 高频考点 | 📌 中英术语见文末
✍️ 配套练习:网页 quizshengxia.dev/java?module=02
2.1 HTTP 报文结构
快递单:面单 + 内件
收快递时,包裹外面贴着一张面单(寄件人、收件人、物品名、订单号),里面才是真正的内件(你买的东西)。HTTP 报文的结构和快递完全一样:最前面是"面单"(起始行 + 头部),空一行之后是"内件"(主体)。起始行、头部、空行、主体四个部分缺一不可,任何一个部分出问题,对方都读不懂你的"信"。
一个原始 HTTP 请求报文长这样:
POST /user/login HTTP/1.1 ← 起始行:方法 + 路径 + 版本
Host: www.example.com ← 请求头:键: 值
Content-Type: application/json
← 空行:头部到此为止
{"username": "tom", "password": "123"} ← 请求体
一个原始 HTTP 响应报文长这样:
HTTP/1.1 200 OK ← 起始行:版本 + 状态码 + 原因短语
Content-Type: text/html; charset=utf-8 ← 响应头
← 空行
<html><body>登录成功</body></html> ← 响应体
注意那个空行:它是头部和主体的分界线,绝对不能省。服务器解析报文时,先读到空行认为"头部读完了",空行之后的内容才当成主体。如果漏掉空行,整个报文会被当成头部解析,直接报错。
💡 记忆口诀:报文 = 面单 + 空行 + 内件。起始行是"谁给谁什么",头部是附带说明,空行是分界线,主体是真正的东西。
报文的四个部分
| 部分 | 请求里的角色 | 响应里的角色 |
|---|---|---|
| 起始行 | 方法 + URL + 版本 | 版本 + 状态码 + 原因短语 |
| 头部(Header) | 说明请求的附加信息 | 说明响应的附加信息 |
| 空行 | 分隔头部与主体 | 分隔头部与主体 |
| 主体(Body) | 提交的数据(POST 才有) | 真正的网页 / 文件内容 |
注意请求和响应起始行的顺序正好相反:请求是"方法 + 地址 + 版本",响应是"版本 + 状态码 + 说明"。为什么?因为请求要告诉服务器"我想干嘛",响应要告诉浏览器"结果如何"——各自最关心的信息放在最前面,解析起来最快。
⚠️ 常见错误
- 漏掉空行:自己拼报文时忘了头部和主体间的空行,服务器报
400 Bad Request。为什么会这样?空行是头部的结束标志,没有它服务器不知道头部什么时候结束。怎么避免?写调试代码或抓包分析时,记住"头部之后必然有一个空行"。 - 头部写成中文冒号:HTTP 头必须是英文
键: 值(英文冒号 + 空格),用中文":"或漏掉空格解析都会失败。为什么?HTTP 协议规范里规定分隔符是 ASCII 冒号,中文冒号是两个字节,完全不匹配。怎么避免?所有请求头都用英文标点。 - 以为请求必须有主体:GET 请求一般没有主体,参数放 URL 里;只有 POST/PUT 才有主体。为什么容易错?因为表单提交 POST 时经常有主体,初学者就以为所有请求都有。怎么判断?看方法:GET 主体通常为空,POST/PUT 才有。
2.2 请求方法:GET / POST / PUT / DELETE ⭐
图书馆借阅:读、借、还、续期
把服务器上的资源想成图书馆的书,四种常用方法就是四种不同操作:GET 是翻书看内容(不借走)、POST 是把一本新书登记进馆、PUT 是把书架上某本书整个换掉、DELETE 是把某本书撤出图书馆。想清楚"这次操作是读、是加、是整体换、还是删",你自然就选对了方法。
| 方法 | 语义 | 生活类比 | 典型场景 | 是否有主体 |
|---|---|---|---|---|
| GET | 读取资源 | 翻书看 | 打开网页、搜索、看文章 | 通常无 |
| POST | 创建资源 | 登记新书 | 注册账号、发表评论、提交表单 | 有 |
| PUT | 整体替换 | 换掉整本书 | 更新整个用户资料 | 有 |
| DELETE | 删除资源 | 撤掉书 | 删除订单、注销账号 | 通常无 |
这四个方法对应着对资源的四种基本操作,业界称为 CRUD(Create 增、Read 读、Update 改、Delete 删)。写接口时把 CRUD 和这四个方法对应起来,接口风格就统一了,别人一看就懂。
GET 与 POST 的本质区别(常考)
GET 和 POST 是课程里最常被对比的一对,从四个维度记牢:
- 语义:GET 是"取",不应有副作用(多取几次结果一样);POST 是"送",让服务器干活、改数据。
- 参数位置:GET 参数放在 URL 的查询字符串里(
?name=tom),会暴露在地址栏、留在历史记录里;POST 参数放在请求体里,不显示在地址栏。 - 数据大小:URL 长度有限制(浏览器和服务器一般约定几 KB),GET 传不了大内容;POST 用请求体,能传大文件、大文本。
- 安全性:两者本身都不加密(要加密得上 HTTPS)。但 GET 参数会进服务器日志和浏览器历史,敏感信息绝不能用 GET 传。
用 Java 伪代码体会服务器如何区分这两个方法:
// 伪代码:服务器根据方法决定走"读"还是"写"
void service(请求 req, 响应 resp) {
String 方法 = req.getMethod(); // 拿到 "GET" 或 "POST"
if ("GET".equals(方法)) {
查数据库(req); // GET:只读,不修改数据
resp.setStatus(200);
} else if ("POST".equals(方法)) {
写入数据库(req); // POST:带新数据去改动
resp.setStatus(201); // 创建成功用 201 更贴切
}
}
💡 记忆口诀:GET 是"看"、POST 是"干"。看的东西地址栏能看见,干的事藏进请求体。PUT 整体换、DELETE 整个删。
⚠️ 常见错误
- 用 GET 传密码:密码会暴露在地址栏、浏览器历史、服务器日志里,等于写在明处。为什么危险?日志一旦泄露,密码全跟着泄露。怎么避免?登录、支付等敏感操作一律用 POST。
- 以为 POST 比 GET 安全:POST 只是不显示在地址栏,抓包照样能看到明文。为什么容易误解?因为地址栏看不见,给人"隐藏"的错觉。怎么避免?真正的安全靠 HTTPS 加密,而不是换方法。
- GET 传超长内容:把一大段文章塞进 URL,超过长度限制直接被服务器拒绝或截断。为什么?URL 本身有长度上限,浏览器和服务器都会卡。怎么避免?大内容用 POST 放请求体。
- POST 提交后刷新重复提交:POST 提交后按 F5,浏览器会提示"要重新提交表单吗",点确认可能产生重复数据。为什么?浏览器重放上一次的请求,而 POST 是有副作用的。怎么避免?服务端做好幂等处理,或提交成功后重定向到结果页(PRG 模式)。
2.3 状态码:1xx - 5xx ⭐
考试成绩单:数字对号入座
状态码是服务器在响应里告诉浏览器"这单处理得怎么样",就像成绩单上的等级:A 优秀、C 及格、F 挂科。HTTP 把结果分成五大类,第一位数字决定类别:
| 分类 | 含义 | 一句话理解 |
|---|---|---|
| 1xx | 信息 | 已收到,继续处理(很少见) |
| 2xx | 成功 | 搞定,这是结果 |
| 3xx | 重定向 | 别停这,去别处 |
| 4xx | 客户端错误 | 你(浏览器)那边有问题 |
| 5xx | 服务器错误 | 我(服务器)这边出事了 |
常见状态码逐个记
| 状态码 | 含义 | 生活类比 |
|---|---|---|
| 200 | 成功,一切正常 | 取餐成功 |
| 301 / 302 | 永久 / 临时重定向 | 店铺永久搬家 / 临时搬走 |
| 304 | 资源没变,用本地缓存 | 东西没变,不用再拿一份 |
| 400 | 报文格式不对 | 你说话我没听懂 |
| 401 / 403 | 未认证(没登录)/ 无权限 | 没带证件 / 证件有了但没资格 |
| 404 | 资源不存在 | 店里没有这道菜 |
| 500 / 502 | 服务器异常 / 网关拿不到上游响应 | 后厨起火 / 外卖平台联系不上餐厅 |
3xx 值得单独说:301 是"永久搬家",浏览器会把书签里的地址自动更新成新地址;302 是"临时搬走",这次去新地址,下次还走老地址也没问题。304 和它们不一样,它是"内容没变",让浏览器直接用本地缓存,省流量、省时间,是性能优化的常用手段。
💡 记忆口诀:4 打头怪自己,5 打头怪服务器。看到 404 先检查 URL 和文件路径,看到 500 去翻后端日志。
⚠️ 常见错误
- 把 401 和 403 混了:401 是"没凭证"(没登录),403 是"有凭证但没权限"(登录了也不让看)。为什么容易混?两个都是"拒绝访问"。怎么区分?先问一句"对方知道我是谁吗"——不知道就是 401,知道但拒绝就是 403。
- 看到 500 只刷新浏览器:500 是服务器程序抛异常,刷新没用。为什么?异常发生在服务器内部,浏览器再怎么重试都一样。怎么避免?去服务器日志(Tomcat 的
logs/catalina.out)里找异常堆栈,定位是哪行代码抛的。 - 忘记 304 的含义:304 不是错误,是"用缓存"。为什么容易误判?看到不是 2xx 就紧张。怎么理解?响应里没有主体,浏览器用本地副本,这正是缓存命中的标志,是好事。
2.4 常见请求头与响应头
快递面单的"备注栏"
快递面单除了地址还有"备注栏":加急、易碎、到付。HTTP 头部就是报文的"备注栏",一行一个 键: 值,给对话双方补充说明,不参与正文。头部有成百上千种,但常考的就那么几个,逐个记牢。
常用请求头(浏览器发给服务器的说明):
| 请求头 | 作用 | 例子 |
|---|---|---|
Host |
告诉服务器访问的是哪个域名(必须有) | Host: www.example.com |
User-Agent |
我是哪种浏览器 / 设备 | User-Agent: Mozilla/5.0 |
Content-Type |
请求体的类型(POST 提交时尤其重要) | Content-Type: application/json |
Accept |
我希望能收到什么类型的响应 | Accept: text/html, application/json |
Cookie |
把我以前存的"小纸条"带给服务器 | Cookie: sessionId=abc123 |
常用响应头(服务器给浏览器的说明):
| 响应头 | 作用 | 例子 |
|---|---|---|
Content-Type |
响应体是什么类型、什么编码 | Content-Type: text/html; charset=utf-8 |
Content-Length |
响应体有多少字节 | Content-Length: 58 |
Set-Cookie |
让浏览器存一张"小纸条"(Cookie) | Set-Cookie: sessionId=abc123; Max-Age=3600 |
Location |
配合 3xx 重定向,告诉浏览器"去这个新地址" | Location: /login.html |
为什么 Content-Type 请求头和响应头都要看:请求里的告诉服务器"我发给你的是 JSON 还是表单";响应里的告诉浏览器"返回的是 HTML 还是图片、用什么编码"。中文乱码十有八九是响应头没写 charset=utf-8——服务器返回的中文是 UTF-8 编码,浏览器却用别的编码去解读,自然变乱码。
💡 记忆口诀:请求头是"我的请求说明",响应头是"我的响应说明";
Content-Type是双方报格式、报编码的窗口,中文乱码先看它。
⚠️ 常见错误
- 请求头漏写
Host:HTTP/1.1 规定Host必须存在,漏了会直接报 400。为什么?一台服务器上可能托管几十个网站,靠Host区分要访问哪一个。怎么避免?浏览器会自动带,但用 curl 或手写请求时别漏。 Content-Type和实际内容对不上:声明了application/json却发a=1&b=2这种表单格式,服务器按 JSON 解析直接报错。为什么?服务器按头部声明的类型解析主体。怎么避免?发什么格式就声明什么类型,前后保持一致。- 响应头忘写
charset=utf-8:中文全部变成问号或乱码。为什么?浏览器不知道用什么编码解读,只能用默认编码(可能是 GBK 或 ISO-8859-1)。怎么避免?setContentType里带上; charset=utf-8。
2.5 无状态协议、Keep-Alive、HTTPS
无状态:快餐店不记得你上次点过什么
HTTP 是无状态协议(stateless):服务器处理完一个请求就"失忆"了,不记得你上次来过、点过什么。就像快餐店排队——你上回点了什么,店员这次完全不知道,每次都是从头来。这个设计让服务器不用为每个用户维护记忆,架构简单、能扛并发。
但"失忆"带来了现实问题:购物车怎么记住?登录状态怎么保持?于是有了 Cookie:服务器通过 Set-Cookie 给你一张"小纸条",你下次带着它来,它就知道"哦,是你"。一来一回的过程:
第一次:浏览器 →(无 Cookie)→ 服务器
服务器 → Set-Cookie: sessionId=abc → 浏览器存下纸条
第二次:浏览器 → Cookie: sessionId=abc → 服务器认出你了
注意两个头方向相反:Set-Cookie 是响应头(服务器给浏览器),Cookie 是请求头(浏览器还给服务器),一存一取,正好闭环。
💡 记忆口诀:无状态 = 答完就失忆;Cookie = 失忆后的"小纸条"。
Set-Cookie存、Cookie取,方向别记反。
Keep-Alive:长连接省得反复握手
HTTP/1.1 默认开启 Keep-Alive(长连接)。一个网页要加载 HTML、CSS、图片、JS 十几个文件,如果每个请求都"新建连接 → 用完断开",光建立连接的三次握手开销就够受的。Keep-Alive 让一条连接保持一段时间,多个请求共用它,像打一个电话说好多件事,省去反复拨号的成本。
| 对比 | 短连接 | Keep-Alive 长连接 |
|---|---|---|
| 连接数 | 每个请求都新开 | 一条连接复用 |
| 效率 | 低,握手开销大 | 高,省握手 |
| 服务器压力 | 连接频繁建立销毁 | 连接保持占用资源 |
| 适用场景 | 请求量极少的场景 | 网页这种多资源场景 |
HTTP 与 HTTPS:加了一把锁
HTTPS = HTTP + TLS/SSL 加密。HTTP 的报文是明文,在公共 Wi-Fi 上,中间人(同一个网络里的攻击者)能直接看到你发的密码;HTTPS 在 HTTP 外面套了一层加密,报文变成密文,中间人看到的是乱码,只有服务器能解开。
| 对比 | HTTP | HTTPS |
|---|---|---|
| 默认端口 | 80 | 443 |
| 传输 | 明文 | 加密(TLS/SSL) |
| 证书 | 不需要 | 需要 CA 签发的证书 |
| 适用场景 | 不敏感页面 | 登录、支付等敏感场景 |
除了加密,HTTPS 还有一层"身份验证":证书由 CA 机构签发,浏览器验证证书有效才继续。这就好比寄信时不仅要密封,还要确认收件人身份,双保险。
💡 记忆口诀:HTTP 是明信片(谁都能读),HTTPS 是密封信(只有收件人能拆)。默认端口一个 80 一个 443。
⚠️ 常见错误
- 把无状态理解成"服务器没内存":无状态指"服务器不主动记你的上次请求",不是服务器不能存数据,数据库、文件系统照常存。为什么容易错?"无状态"这个词太容易望文生义。怎么避免?记住"无状态"针对的是"请求与请求之间不自动关联",而不是"不能存数据"。
- 混淆
Cookie和Set-Cookie:Cookie是请求头(浏览器带给服务器),Set-Cookie是响应头(服务器让浏览器存)。为什么容易混?两个单词太像。怎么避免?记"服务器 Set(设置),浏览器 Cookie(携带)",方向是反的。 - 以为长连接永不关闭:Keep-Alive 有超时时间,闲置久了服务器也会断。为什么?连接长期占用不释放会耗尽服务器资源。怎么避免?把超时时间设置得合理,别设成无限大。
- 以为 HTTPS 会让网站慢很多:TLS 握手确实有开销,但现代硬件和会话复用机制下影响很小。为什么有人担心?早期 HTTPS 确实有明显开销,传言流传至今。怎么避免?按需求选型,敏感业务直接上 HTTPS,别为省这点时间牺牲安全。
📌 双语术语表(本讲)
| 中文 | English | 记忆点 |
|---|---|---|
| 报文 | Message | HTTP 对话的"信" |
| 起始行 | Start Line | 报文的"标题" |
| 头部 | Header | 报文的"备注栏" |
| 请求体 / 响应体 | Request / Response Body | POST 送的数据 / 返回的内容 |
| 请求方法 | Request Method | 对资源的操作 |
| 状态码 | Status Code | 处理结果的三位数字 |
| 重定向 | Redirect | 3xx:去别处拿 |
| 客户端错误 | Client Error | 4xx:怪请求方 |
| 服务器错误 | Server Error | 5xx:怪服务器 |
| 无状态 | Stateless | 答完就失忆 |
| 长连接 | Keep-Alive | 一条连接多用几次 |
| 安全超文本传输协议 | HTTPS | HTTP + 加密 |
| Cookie | Cookie | 服务器发的"小纸条" |
| 明文 | Plaintext | 没加密的内容,谁都能读 |
⭐ 本讲考点清单
- HTTP 报文四部分:起始行、头部、空行、主体,空行是头与主体的分界线。
- GET 用于读取、参数放 URL、无主体;POST 用于提交、参数放请求体、能传大内容。
- 四个方法对应 CRUD:GET 读、POST 增、PUT 改、DELETE 删。
- GET 和 POST 都不加密,真正保安全的是 HTTPS,不是换方法。
- 状态码按首位分类:1xx 信息、2xx 成功、3xx 重定向、4xx 客户端错误、5xx 服务器错误。
- 高频状态码 200/301/302/304/400/401/403/404/500/502 的含义要能说出。
- 401 是"没登录",403 是"登录了没权限",两者不要混淆。
- 请求头
Host、Content-Type、Cookie和响应头Set-Cookie、Location的用途要分清。 Content-Type决定响应编码,中文乱码先检查是否写了charset=utf-8。- HTTP 是无状态协议,靠 Cookie 携带"小纸条"让服务器认人。
- Keep-Alive 长连接让多个请求共用一条连接,减少握手开销。
- HTTPS = HTTP + TLS/SSL 加密,默认端口 443,需要 CA 证书。