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

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

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

学习

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

更多

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

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
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综合练习

方法与语义

GET/POST/PUT/PATCH/DELETE、幂等性。

30 分钟

方法与语义

这一课你将能做到

  1. 说出 GET / POST / PUT / PATCH / DELETE 各自「对资源做什么」,并为本站 /api/tracks、/admin/tracks 一类路径配上正确方法
  2. 用自己的话区分安全方法与幂等,并判断「连点两次提交按钮」会不会重复创建数据
  3. 用 curl 对同一 URL 发错方法,观察 405 Method Not Allowed,并说出重试策略与方法幂等性的关系

为什么要学

前端把「查列表」写成 fetch(url, { method: 'POST' })、后端只开了 @GetMapping,就会出现「接口文档能看、实际 405」。REST 与主流框架的约定是:URL 点名资源,方法点名动作。方法选错,连锁反应在后面:网关不敢缓存你的 POST、重试机制可能重复下单、爬虫和浏览器预取可能误触副作用——把「扣积分」做成 GET 是真实存在过的生产事故模式。

面试中「GET 和 POST 的区别」几乎是必考题,答「一个获取一个提交」只能及格,答出安全性与幂等性才算懂行。本课对齐 MDN / RFC 9110 的日常语义,不背全书。

先建立图像

方法人话幂等?安全?典型 body
GET只读拿资源是是通常无
HEAD只要头、不要 body是是无
POST创建资源或触发一次动作否否常有 JSON
PUT用这份内容整体替换资源是否有
PATCH改一部分字段视设计否有
DELETE删掉资源是否常无
OPTIONS问「允许哪些方法/CORS」是是常无
安全  ≈ 「只读,不改服务器状态」(GET/HEAD/OPTIONS)
幂等  ≈ 「同样请求做 N 次,资源终态和做 1 次一样」

GET 两次     → 还是同一份课表(幂等)
POST 创建两次 → 两条记录(非幂等!)
PUT 替换两次  → 还是同一份内容(幂等)
DELETE 两次   → 第二次常 404,但资源终态仍是「已删除」(视为幂等)

跟着做

步骤 1:把上一课的 Spring Boot 接口补全

在 HelloController 同级加一个演示用的资源控制器(用内存 Map 代替 MySQL,语义与真实 CRUD 一致):

@RestController
@RequestMapping("/api/notes")
public class NoteController {

    private final Map<Long, String> store = new ConcurrentHashMap<>();
    private final AtomicLong idGen = new AtomicLong();

    @GetMapping                 // 读集合
    public Map<Long, String> list() { return store; }

    @GetMapping("/{id}")        // 读单个
    public ResponseEntity<String> get(@PathVariable Long id) {
        return store.containsKey(id)
            ? ResponseEntity.ok(store.get(id))
            : ResponseEntity.notFound().build();
    }

    @PostMapping                // 创建
    public ResponseEntity<Long> create(@RequestBody String content) {
        Long id = idGen.incrementAndGet();
        store.put(id, content);
        return ResponseEntity.created(URI.create("/api/notes/" + id)).body(id);
    }

    @PutMapping("/{id}")        // 整体替换
    public String replace(@PathVariable Long id, @RequestBody String content) {
        store.put(id, content);
        return "replaced";
    }

    @DeleteMapping("/{id}")     // 删除
    public ResponseEntity<Void> remove(@PathVariable Long id) {
        store.remove(id);
        return ResponseEntity.noContent().build();
    }
}

重启后按下面的步骤逐个验证。

步骤 2:GET 连发两次,体会幂等

curl -s http://localhost:8080/api/notes
curl -s http://localhost:8080/api/notes

两次结果完全一致——无论发多少次,服务器状态不变,这就是幂等 + 安全的直观感受。

步骤 3:POST 连发两次,观察重复创建

curl -s -X POST http://localhost:8080/api/notes -H "Content-Type: application/json" -d "first"
curl -s -X POST http://localhost:8080/api/notes -H "Content-Type: application/json" -d "first"
curl -s http://localhost:8080/api/notes

注意返回了两个不同的 id——同一内容创建了两次。这就是「POST 非幂等」:用户双击提交按钮、网络超时自动重试,都会放大这个问题。生产上支付/下单类接口会加幂等键(客户端生成唯一 ID,服务端去重)。

步骤 4:对只读接口误用错误方法,看 405

# 对集合发 DELETE?我们的 NoteController 没有 DELETE /api/notes 映射
curl -i -X DELETE http://localhost:8080/api/notes

会看到 405 Method Not Allowed,响应头里可能带 Allow: GET, POST——服务器在告诉你「资源在,但这个动词不行」。对照状态码课:405 ≠ 404,前者是方法错,后者是资源无。

步骤 5:PUT 与幂等验证

# 同一条替换请求发两次
curl -s -X PUT http://localhost:8080/api/notes/1 -H "Content-Type: application/json" -d "v2"
curl -s -X PUT http://localhost:8080/api/notes/1 -H "Content-Type: application/json" -d "v2"
curl -s http://localhost:8080/api/notes/1

发 10 次结果都是 v2——「替换」天然幂等,所以客户端超时重试 PUT 是安全的;这也是 HTTP 客户端库默认只自动重试幂等请求的原因。

步骤 6:对照 Spring 注解心智

HTTP 方法Spring 注解本站课表示例
GET@GetMappingGET /api/tracks 读课表
POST@PostMapping + @RequestBodyPOST /admin/tracks 新建 track
PUT@PutMappingPUT /admin/tracks/{id} 全量更新
PATCH@PatchMappingPATCH /admin/tracks/{id} 只改 title
DELETE@DeleteMappingDELETE /admin/tracks/{id} 下架

设计口诀:读 GET,建 POST,整换 PUT,局部 PATCH,删 DELETE。

步骤 7:想一遍「副作用藏在 GET 里」的事故

假设有人把「领取积分」实现成 GET /api/points/claim?userId=1:浏览器会预取链接、爬虫会抓取、代理可能重试、用户刷新页面会再领一次。正确做法是 POST /api/points/claim,GET 只用于 GET /api/points/{userId} 查余额。把这条写进团队代码评审清单,比事后追幂等键便宜得多。

讲清楚要点

  1. 因为方法表达意图,所以网关和缓存可以按方法做不同策略——GET/HEAD 可被缓存与预取,POST 默认不被任何一层缓存;把查询做成 POST 等于主动放弃整条缓存链路。
  2. 因为 GET/HEAD/OPTIONS 被定义为安全方法,所以浏览器、爬虫、代理敢于自动发起或重试它们——永远不要把写操作藏在 GET 里。
  3. 因为 PUT/DELETE 幂等,客户端超时重试相对安全;重试 POST 可能重复下单——支付类接口必须显式设计幂等(幂等键、唯一索引、状态机),不能指望协议兜底。
  4. 因为 URL 应是名词资源,所以少写 /doCreateTrack 这类动词路径——动作放在方法上,REST 课展开。
  5. 因为 405 表示「资源在、方法不对」,所以排错时先确认后端是否映射了该动词——对照 @XxxMapping 与 Allow 响应头,别一律当 404 处理。
  6. 因为 PATCH 语义不如 PUT 统一(补丁格式需约定,如 JSON Merge Patch),所以团队要么统一用 PUT + 全量 DTO,要么明确定义 PATCH 格式——最怕两种混用还没文档。

你可能会懵的地方

  1. PUT 和 PATCH 到底用哪个? 整份文档替换 → PUT;只改 title 一个字段 → PATCH。很多内部 API 只用 PUT + 全量对象,也完全可以——关键是前后端一致,别让 PUT 实际只收部分字段。

  2. POST 一定表示「创建」吗? 不必须。POST 是最泛化的「处理这个数据」:登录(POST /api/auth/login)、触发导出、大条件搜索(POST /api/tracks/search)都合法。创建集合成员只是它最常见的用法。

  3. 幂等是要求「响应字节级相同」吗? 不是。幂等看的是资源终态:第二次 DELETE 返回 404,但「资源不存在」这个终态没变,语义上仍幂等。

  4. HTML 表单只支持 GET/POST,怎么发 PUT/DELETE? 这是 HTML 表单的历史限制,不是 HTTP 的限制。现代前端用 fetch/axios 可以发任何方法。别用「表单只支持两种」来约束 API 设计。

  5. OPTIONS 平时用不到,为什么要认识它? 你不主动发,浏览器会替你发——CORS 预检就是 OPTIONS(CORS 课细讲)。后端没正确响应 OPTIONS,前端会表现为「接口假死」;另外它也可用于探活:curl -i -X OPTIONS -H "Origin: https://x.com" url 看 Allow 头。

错法对照

错法问题正解
用 GET 带 body 做复杂查询缓存/代理/日志对 GET body 的处理不确定,且可能被预取重复执行简单过滤用查询参数;超大条件定义 POST /search 端点并写进文档
所有写操作都叫 POST /api/update方法语义丢失,网关无法区分创建/更新/删除PUT/PATCH/DELETE /api/tracks/{id},动作归方法管
前端请求超时就自动重试所有 POST可能重复创建/重复扣款只自动重试幂等方法;POST 重试需幂等键
出现 405 只会重启服务405 说明方法没映射上,重启无效查 Allow 响应头 + 对照 Controller 注解
把「查询」做成 POST 理由是「参数复杂」放弃了缓存、预取、可收藏性,日志排查也变难查询参数承载过滤;确实巨大再用 POST 搜索端点

卡点急救

  1. POST 报 415 Unsupported Media Type:请求头缺 Content-Type: application/json,Spring 的 @RequestBody 不知道怎么反序列化。curl 补 -H "Content-Type: application/json";浏览器 fetch 里 headers: { 'Content-Type': 'application/json' }。
  2. DELETE/PUT 返回 405,但代码里明明写了注解:确认类上 @RequestMapping("/api/notes") 与方法上 @DeleteMapping("/{id}") 拼接后路径是否真的一致;再看是否被安全框架(如 Spring Security)拦截后转成了别的响应。开 Debug 日志 logging.level.org.springframework.web=DEBUG 看实际映射。
  3. 前端连点按钮创建了多条数据:短期方案按钮置灰 + 防抖;根治是后端幂等——数据库唯一约束或幂等键表,客户端为每次业务操作生成固定 Idempotency-Key 头,服务端见重就返回第一次的结果。

面试常问

  • GET 和 POST 的区别? 分三层答:① 语义:GET 读资源、安全幂等;POST 提交数据、非幂等。② 报文:GET 参数在 URL 查询串,body 通常为空;POST 数据在 body,需 Content-Type 声明格式。③ 工程后果:GET 可被缓存/收藏/预取,POST 不会;GET 参数进服务器访问日志和浏览器历史,敏感数据别放 GET。

  • 什么是幂等性?哪些 HTTP 方法幂等? 同一请求执行一次与执行多次对资源状态的影响相同。GET/HEAD/OPTIONS/PUT/DELETE 幂等,POST 不幂等(PATCH 视实现)。应用价值:客户端重试策略、消息队列重复消费去重、接口防重都建立在幂等之上。

  • 为什么浏览器表单只支持 GET/POST,而 HTTP 定义了那么多方法? HTML 表单规范是 90 年代定下的子集,一直没扩展。HTTP 方法是协议层概念,任何 HTTP 客户端(fetch、axios、curl、OkHttp)都能发全。API 设计按协议语义来,不要被表单限制绑架。

自测三问

  1. 「安全」与「幂等」各用一句话解释,并各举两个方法。
  2. 为什么创建资源常用 POST 到集合路径而不是 PUT?什么情况下 PUT 到确定 URL 也合理?
  3. 对只读的 /api/notes 误发 DELETE,期望什么状态码?如何用 curl 验证并从响应头里找到线索?

小结一句话

方法是资源上的动词:读用 GET、建用 POST、整换用 PUT、局部改用 PATCH、删用 DELETE;安全与幂等决定缓存与重试能不能帮你。

延伸

  • MDN:HTTP 请求方法
  • RFC 9110 §9 Methods(查阅用,不必通读)
  • 下一课:状态码——2xx/3xx/4xx/5xx 四大家族,200/201/204/301/304/401/403/404/500/502 与真实场景对号入座。
加载本节练习…
← 上一课HTTP 是什么下一课 →状态码