CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
HTTP 协议与缓存/课时/HTTP 是什么
RawBlame
课时目录12 节

HTTP 协议与缓存

  1. 01HTTP 是什么
  2. 02方法与语义
  3. 03状态码
  4. 04请求与响应头
  5. 05HTTPS 与 TLS
  6. 06HTTP 缓存
  7. 07Cookie 与会话
  8. 08CORS
  9. 09REST 与资源设计
  10. 10HTTP/2 与 HTTP/3
  11. 11应用层缓存对照
  12. 12综合练习

HTTP 是什么

客户端-服务器、无状态、与 TCP/TLS 关系。

25 分钟

HTTP 是什么

这一课你将能做到

  1. 用自己的话说明一次「打开网页」里谁是客户端、谁是服务器,并说出请求与响应各包含哪几块(请求行 / 请求头 / 空行 / body)
  2. 在本机跑起一个最小 Spring Boot 接口,用 curl -v 抓出完整的请求与响应原文,逐行指出每个部分
  3. 在浏览器 DevTools Network 面板找到同一条请求,对照 curl 与浏览器看到的差异,说清「无状态」和「Content-Type」各指什么

为什么要学

后端写 @GetMapping、前端写 fetch、运维配 Nginx,底层都在说同一种「信封格式」——HTTP。接口报 401、CORS 拦截、缓存不失效,本质都是「信封上某个字段不对」。不会读请求/响应,排错只能靠猜;会读之后,DevTools 里一行就能定位是方法错了、状态码不对还是头缺了。

面试里「讲讲从输入 URL 到页面展示发生了什么」是出现频率最高的问题之一,答案的骨架就是 HTTP 请求/响应模型。本课不堆 RFC 条文,只建立日常开发够用的心智模型:无状态、文本头 + 可选 body、客户端主动发起。

先建立图像

把 HTTP 想成寄包裹:

客户端(浏览器 / curl / 手机 App)
    │  写好「请求信封」:方法 + 路径 + 头 +(可选)正文
    ▼
网络(生产常经 Nginx 等中间层)
    │
    ▼
服务器(Spring Boot / Next.js / 静态文件服务)
    │  回「响应信封」:状态码 + 头 +(可选)正文
    ▼
客户端拆开使用(渲染 HTML / 解析 JSON / 存 Cookie)

一条 HTTP/1.1 报文的解剖图:

GET /api/tracks HTTP/1.1          ← 请求行:方法 + 路径 + 协议版本
Host: coderoadmap.cn              ← 请求头(键值对,到空行为止)
Accept: application/json

(空行:头与 body 的分界线)
(GET 通常没有 body)

HTTP/1.1 200 OK                   ← 状态行:协议版本 + 状态码 + 短语
Content-Type: application/json    ← 响应头
Content-Length: 57

[{"slug":"http-protocol-essentials","title":"HTTP 必修"}]   ← 响应 body
名词人话例子
请求客户端发出的那一包GET /api/tracks
响应服务器回的那一包200 + JSON 数组
无状态协议本身不记得「上一包是谁」登录态要靠 Cookie/Token 每次自己带
资源URL 指向的「东西」/api/tracks 这张课表

协议栈位置(记住分工即可,不必背层号):

应用层   HTTP ← 本 track 全部内容
传输层   TCP(可靠字节流)→ TLS(加密,HTTPS 课细讲)
网络层   IP(寻址)

生产环境常见拓扑:浏览器 ──TLS──► Nginx:443 → 内网明文 HTTP → Spring Boot:8080。

跟着做

步骤 1:在本机跑一个最小 Spring Boot 接口

用 Spring Initializr 建一个只勾 Spring Web 的项目(本站同款技术栈),加一个控制器:

@RestController
public class HelloController {

    @GetMapping("/api/hello")
    public Map<String, String> hello() {
        return Map.of("msg", "hello java-nav");
    }

    @PostMapping("/api/echo")
    public Map<String, Object> echo(@RequestBody Map<String, Object> body) {
        return Map.of("received", body, "at", System.currentTimeMillis());
    }
}

启动:

mvn spring-boot:run
# 看到 Tomcat started on port 8080 即成功

步骤 2:用 curl -v 看信封原文

新开一个终端:

curl -v http://localhost:8080/api/hello

-v(verbose)会把所有细节打出来,> 开头的是客户端发出的,< 开头的是服务器返回的:

> GET /api/hello HTTP/1.1      ← 请求行
> Host: localhost:8080          ← curl 自动补的头
> User-Agent: curl/8.x
> Accept: */*
>
< HTTP/1.1 200                  ← 状态行
< Content-Type: application/json
< Transfer-Encoding: chunked
<
{"msg":"hello java-nav"}        ← body

对照「先建立图像」的解剖图,把四块在输出里圈出来。这一步做完,HTTP 报文就不再是抽象概念。

步骤 3:发一条带 body 的 POST

# -H 设置请求头;-d 设置请求体;POST JSON 必须声明 Content-Type
curl -v -X POST http://localhost:8080/api/echo \
  -H "Content-Type: application/json" \
  -d '{"name":"java-nav","type":"track"}'

观察两点:请求侧多了 Content-Type: application/json 和一行 body;响应仍是 JSON。GET 和 POST 的差别在报文上就是「有没有 body、方法写什么」。

步骤 4:浏览器 Network 面板对照

  1. 打开任意网站(如 https://coderoadmap.cn/tracks),按 F12 → Network 面板
  2. 勾选 Preserve log,按 Ctrl+Shift+R 硬刷新
  3. 点第一行 document 请求,右侧 Headers 分栏里就是浏览器版的报文视图
  4. 对比:浏览器自动补了更多头(sec-ch-ua、Accept-Language 等),但骨架和 curl 看到的一模一样

注意同一动作往往触发几十条请求(HTML、CSS、JS、API 各一条)——一次「打开网页」= 一串独立 HTTP 交换,不是一条请求搞定整站。

步骤 5:故意看一次失败形状

# 请求一个不存在的路径
curl -i http://localhost:8080/api/no-such-path

会看到 404(Spring Boot 默认还有一段 JSON 错误体)。养成习惯:先看状态码,再读 body。状态码是摘要,body 是细节。

步骤 6:对照生产链路

口头复述一遍本站请求路径:

浏览器 → Nginx:443(TLS 在这里结束)
       → /api/* 转发给 Spring Boot:8080 生成 JSON
       → 页面路由转发给前端服务生成 HTML

你 curl 的 localhost:8080 绕过了 Nginx,所以没有 TLS、没有反代追加的头;生产请求则多了一层。排错时要分清「谁收到了这条请求」。

讲清楚要点

  1. 因为 HTTP 是应用层协议,所以 curl、浏览器、Java HttpClient 看到的是同一套「方法/状态/头/body」模型——换工具不换语法,学会一次到处能用。
  2. 因为协议默认无状态,所以服务器不会「自动记得你上次登录」——会话必须靠 Cookie、Authorization 等客户端每次自带的凭证(Cookie 课细讲)。
  3. 因为请求由客户端发起,所以服务端不能凭空「推一条 HTTP」给浏览器——实时推送要靠 SSE/WebSocket 另外的机制;排错永远从「这条请求是谁发起的」问起。
  4. 因为头是元数据、body 是载荷,所以 Content-Type 错了 body 再对也解析失败——前后端必须对「这是 JSON 还是表单」达成一致,这是新手接口联调第一大坑。
  5. 因为中间可能有 Nginx、CDN、代理,所以浏览器看到的 IP/端口未必是真正执行业务的那台进程——生产排错要对照 Nginx 访问日志和 Spring 应用日志两处。
  6. 因为 HTTPS 只是「HTTP 跑在 TLS 加密通道上」,所以方法、状态码、头的语义在 HTTPS 下完全不变——可以分开学,但公网传输必须加密(HTTPS 课细讲)。

你可能会懵的地方

  1. 「无状态」是不是说服务器完全不能存数据? 不是。MySQL、Redis 随便存。「无状态」指的是协议本身不强制在请求之间维持会话——每个请求必须自带完成它所需的全部信息。会话是应用层自己加的(Cookie + Redis Session 等)。

  2. 打开一个页面为什么 Network 里冒出几十条请求? HTML 里还会引用 CSS、JS、字体、图片,页面 JS 还会调 API,每一条都是独立的 HTTP 请求/响应。分析性能按「瀑布图」逐条看,不要当成一坨。

  3. HTTP/2 的线上报文是二进制的,我学的文本模型还有用吗? 有用。HTTP/2 改的是传输编码(二进制分帧),应用语义(方法、状态码、头字段、body)完全不变。DevTools 会自动翻译回熟悉的 Headers 视图,学习阶段用 HTTP/1.1 文本模型完全够。

  4. 为什么 curl 能通、浏览器却报错? 浏览器比 curl 多了同源策略、Cookie 自动管理、混合内容拦截等安全层。curl 没有同源限制,所以「curl 通、浏览器不通」十有八九是 CORS 或 Cookie 问题,后面 CORS 课专讲。

  5. HTTP 报文里的「空行」有什么用? 它是头和 body 的强制分界线。没有空行,解析器不知道头到哪里结束。手写原始报文或看抓包时,空行丢了会直接解析失败。

错法对照

错法问题正解
把「打开网址」当成一次魔法,不分请求/响应排错无从下手先在 Network 找 document,再找它引发的依赖请求
只看 body 不看状态码和 Content-Type把 HTML 错误页当 JSON 解析,报一堆莫名错误排查顺序:状态码 → 头 → body
以为服务器「记得」上次 GET,漏带 Cookie/Token接口突然 401 还以为是 Bug每个请求自带凭证;无凭证就接受 401
用浏览器地址栏测 POST 接口地址栏本质是 GET 导航,永远测不到 POST用 curl 或接口工具发真实方法
把 TCP「连接成功」当成「业务成功」连接层通不代表应用层对业务结果看应用层状态码,不看连接是否建立

卡点急救

  1. curl: command not found:Windows 10 1809+ 自带 curl(在 PowerShell/CMD 直接用);老系统可从 curl.se 下载,或用 Git Bash 自带的 curl。macOS/Linux 原生自带。
  2. Connection refused:Spring Boot 没启动成功,或端口不是 8080。回启动日志找 Tomcat started on port xxx,把 curl 的端口改成实际端口;用 netstat -ano | findstr 8080(Windows)确认监听是否存在。
  3. 终端输出中文乱码:Windows CMD 默认 GBK。PowerShell 里先执行 chcp 65001 切 UTF-8,或改用 Windows Terminal / Git Bash;Spring Boot 侧确认 server.servlet.encoding.charset=UTF-8。

面试常问

  • 「从输入 URL 到页面展示,中间发生了什么?」 主线:DNS 解析 → TCP 三次握手 →(HTTPS 再加 TLS 握手)→ 浏览器发出 HTTP 请求(方法 + 路径 + 头)→ 服务器(可能经 Nginx 反代)处理后返回状态码 + 头 + body → 浏览器解析 HTML,再对其引用的 CSS/JS/图片/API 发起更多 HTTP 请求 → 渲染。答出「多次 HTTP 交换 + 无状态靠 Cookie 补会话」就超过多数人。

  • HTTP 为什么叫「无状态」?带来什么问题?怎么解决? 协议对事务处理没有记忆能力,每个请求独立。问题是登录态、购物车这类「跨请求上下文」无法天然维持。解决方案:Cookie/Session(服务端存状态,客户端带 session id)、Token/JWT(客户端自包含凭证)、或每次请求显式带鉴权头。

  • HTTP 请求报文和响应报文的结构? 请求:请求行(方法/路径/版本)+ 请求头 + 空行 + 可选 body。响应:状态行(版本/状态码/短语)+ 响应头 + 空行 + 可选 body。关键点:头与 body 之间必须有空行分隔;GET 通常无 body,POST/PUT 常有。

自测三问

  1. 用四块结构描述一条 GET /api/tracks:请求行、请求头、状态行、响应头各举一个真实例子。
  2. 为什么说 HTTP 无状态?登录态通常靠什么机制补回来?
  3. 同一站点里 HTML 页面与 JSON API 的 Content-Type 分别是什么?你在 DevTools 里如何确认?

小结一句话

HTTP 是 Web 的信封格式:客户端发请求、服务器回响应;无状态、文本头 + 可选 body、客户端主动——读懂信封,才谈得上修接口。

延伸

  • MDN:HTTP 概述
  • MDN:HTTP 消息
  • 下一课:方法与语义——GET/POST/PUT/DELETE 各自对资源做什么,「安全」和「幂等」两个词决定缓存与重试能不能帮你。
加载本节练习…
下一课 →方法与语义