📌PDF:大白话说Java面试题 — 10_网络协议篇
第8题:GET 和 POST 的区别
📚回答:
- 核心考点: GET 和 POST 的区别是 HTTP 面试的"送分题",但大厂面试官不会满足于"GET 参数在 URL,POST 参数在 Body"这种表层回答,而是深入考察HTTP 语义(RFC 7231 的规范定义)、幂等性与安全性(RESTful 设计的核心原则)、GET 能否带 Body / POST 能否带 URL 参数(协议边界的精确理解)、TCP 数据包差异(GET 1 个包 vs POST 2 个包的真相与例外)、缓存机制(浏览器缓存、代理缓存、CDN 缓存的完整链路)、以及生产环境中的幂等性设计(防止重复提交)。面试官真正想判断的是:你是否理解 GET 和 POST 的本质差异是语义而非技术实现。
1. 核心区别:语义是本质,实现是表象
RFC 7231 明确定义了 HTTP 方法的语义。GET 和 POST 的根本差异在于设计意图,而非技术细节:
| 维度 | GET | POST |
|---|---|---|
| RFC 语义 | 获取(Retrieve)资源 | 提交(Submit)数据,创建/处理资源 |
| 安全性 | ✅ 安全(不修改资源状态) | ❌ 非安全(可能修改资源状态) |
| 幂等性 | ✅ 幂等(多次执行效果相同) | ❌ 非幂等(多次执行效果可能不同) |
| 可缓存 | ✅ 默认可缓存 | ❌ 默认不可缓存(可手动设置) |
| 参数位置 | URL 查询参数(query string) | 请求体(Body) |
| 数据可见性 | 暴露在 URL,易泄露 | 隐藏在 Body,相对安全 |
| URL 长度限制 | 受浏览器/服务器限制(通常 2~8KB) | 无明确限制(受服务器配置限制) |
| 编码支持 | 仅 ASCII(URL 编码后) | 任意二进制(支持多种 Content-Type) |
| 书签/分享 | ✅ URL 可收藏、可分享 | ❌ 不可收藏(数据在 Body) |
| 浏览器行为 | 刷新/后退无警告 | 刷新会触发重复提交警告 |
关键认知:GET 和 POST 的区别不是"参数在 URL 还是 Body",而是语义上的获取 vs 提交。技术上 GET 也可以带 Body(RFC 不禁止,但不推荐),POST 也可以带 URL 参数(常见做法),但这样做违背了设计意图,可能导致兼容性问题。
2. 安全性与幂等性:RESTful 设计的基石
2.1 安全性(Safe)指方法是否修改服务器资源状态。GET 是安全的,因为它只读取资源,不修改;POST 是非安全的,因为它可能创建或修改资源。
注意:安全性 ≠ 数据传输安全。HTTP 明文传输下,GET 和 POST 都不安全,必须使用 HTTPS。
2.2 幂等性(Idempotent)指多次执行相同请求,服务器资源状态是否一致。这是分布式系统设计的核心概念。
方法 是否安全 是否幂等 典型副作用 GET ✅ 是 ✅ 是 无 HEAD ✅ 是 ✅ 是 无 OPTIONS ✅ 是 ✅ 是 无 PUT ❌ 否 ✅ 是 全量更新资源 DELETE ❌ 否 ✅ 是 删除资源 POST ❌ 否 ❌ 否 创建新资源(每次创建不同资源) PATCH ❌ 否 ⚠️ 视实现 部分更新资源 幂等性的工程意义:
- 幂等方法可安全重试(网络超时自动重发不会导致副作用);
- 幂等方法可被缓存(代理和 CDN 可安全缓存响应);
- 幂等方法可被预加载(浏览器预加载链接不会破坏状态)。
POST 的幂等性陷阱:
POST /orders {amount: 100} → 第一次:创建订单 #1001 POST /orders {amount: 100} → 第二次:创建订单 #1002(非幂等!)解决方案:使用幂等键(Idempotency Key),客户端生成唯一键(如 UUID),服务端用 Redis 缓存"键 → 结果",重复请求直接返回缓存结果。
3. 技术实现细节
3.1 GET 能否带 Body?POST 能否不带 Body?
问题 答案 说明 GET 能否带 Body? ✅ 可以(RFC 不禁止) 但大多数服务器/代理/库不支持,Elasticsearch 等少数场景使用 POST 能否不带 Body? ✅ 可以 如 POST /logout仅触发动作,无数据POST 能否带 URL 参数? ✅ 可以 常见做法,如 POST /orders?source=app最佳实践:遵循语义,GET 用 URL 参数,POST 用 Body,避免兼容性问题。
3.2 TCP 数据包差异:GET 1 个包 vs POST 2 个包?
广为流传的说法:“GET 产生 1 个 TCP 数据包,POST 产生 2 个(先发送 Header,服务器响应 100 Continue,再发送 Body)”。
真相:
- GET 和 POST 的 TCP 数据包数量没有本质区别,都取决于数据量和 TCP 分段;
- POST 的 2 个包仅在启用
Expect: 100-continue时出现:- 客户端发送 Header +
Expect: 100-continue; - 服务器响应
100 Continue; - 客户端发送 Body。
- 客户端发送 Header +
- 大多数 POST 请求不启用
Expect: 100-continue,直接在一个 TCP 报文中发送 Header + Body; - GET 请求如果 URL 很长,同样会被拆分为多个 TCP 报文。
// 启用 Expect: 100-continue 的 POST(Java)HttpURLConnectionconn=(HttpURLConnection)url.openConnection();conn.setRequestMethod("POST");conn.setRequestProperty("Expect","100-continue");// 显式启用conn.setDoOutput(true);// 此时先发送 Header,等待 100 Continue 后再发送 Body3.3 缓存机制差异
缓存层级 GET POST 浏览器缓存 ✅ 默认缓存(Cache-Control、ETag、Last-Modified) ❌ 默认不缓存 代理缓存 ✅ 可缓存 ❌ 不可缓存 CDN 缓存 ✅ 可缓存 ❌ 不可缓存 GET 的缓存条件:
- 响应头包含
Cache-Control: public或max-age; - 无
Cache-Control: no-store; - 请求无
Authorization(除非响应含Cache-Control: public)。
POST 缓存的例外:
POST /search HTTP/1.1 Cache-Control: max-age=3600 响应:Cache-Control: public, max-age=3600极少数场景下 POST 可缓存(如搜索接口),但需显式设置,且不推荐。
- 响应头包含
4. 生产环境:幂等性设计实践
4.1 重复提交问题用户点击"提交订单"后网络延迟,用户再次点击,导致重复扣款/重复创建订单。
解决方案对比:
方案 实现 适用场景 前端防抖 点击后禁用按钮 N 秒 简单场景,不可靠(F5 刷新可绕过) Token 机制 页面加载时获取唯一 Token,提交时校验 表单提交 幂等键(Idempotency Key) 客户端生成 UUID,服务端 Redis 缓存结果 API 接口、分布式系统 数据库唯一索引 订单号唯一,重复插入报错 最终一致性兜底 幂等键最佳实践:
// 客户端StringidempotencyKey=UUID.randomUUID().toString();headers.set("X-Idempotency-Key",idempotencyKey);// 服务端@PostMapping("/orders")publicResponseEntity<?>createOrder(@RequestHeader("X-Idempotency-Key")Stringkey,@RequestBodyOrderRequestrequest){// 1. 检查 Redis 是否已处理StringcachedResult=redisTemplate.opsForValue().get("idempotency:"+key);if(cachedResult!=null){returnResponseEntity.ok(cachedResult);// 直接返回缓存结果}// 2. 执行业务逻辑Orderorder=orderService.create(request);// 3. 缓存结果(设置过期时间,如 24 小时)redisTemplate.opsForValue().set("idempotency:"+key,JsonUtils.toJson(order),Duration.ofHours(24));returnResponseEntity.ok(order);}4.2 GET 误用的安全隐患
误用场景 风险 正确做法 GET /delete?id=123浏览器预加载、爬虫误触发删除 改用 DELETE /resources/123GET /transfer?to=xxx&amount=100URL 参数泄露,CSRF 攻击 改用 POST+ CSRF Token敏感信息放 URL 浏览器历史、服务器日志、Referer 泄露 改用 POST+ Body
5. 常见误区澄清
| 误区 | 正确理解 |
|---|---|
| “POST 比 GET 更安全” | HTTP 下两者都不安全,HTTPS 下两者都安全。POST 的 Body 虽不在 URL,但仍可被中间人窃听(无 HTTPS 时) |
| “GET 只能传 ASCII,POST 可以传二进制” | 是 URL 编码的限制,不是 GET 本身的限制。GET 用 Base64 编码同样可以传二进制 |
| “GET 有长度限制,POST 没有” | HTTP 协议本身无限制,限制来自浏览器(URL 长度)和服务器配置(Body 大小) |
| “GET 用于获取,POST 用于提交” | 这是语义规范,不是技术强制。技术上 GET 可以提交,POST 可以获取,但违背规范 |
| “POST 产生 2 个 TCP 包” | 仅在Expect: 100-continue时成立,默认情况下 POST 和 GET 一样都是 1 个包 |
6. 面试官追问与高分回答模板
追问 1:“GET 和 POST 的区别是什么?”
低分回答:“GET 参数在 URL,POST 参数在 Body;GET 有长度限制,POST 没有;GET 不安全,POST 安全。”(全是表象,没有触及语义)
高分回答:
"GET 和 POST 的本质区别是语义:GET 用于获取资源,POST 用于提交数据创建/处理资源。这个语义差异衍生出五个关键区别:
- 安全性:GET 是安全的(不修改资源状态),POST 是非安全的(可能修改状态);
- 幂等性:GET 是幂等的(多次执行效果相同),POST 是非幂等的(多次执行可能创建多个资源);
- 缓存:GET 默认可缓存,POST 默认不可缓存;
- 参数位置:GET 参数在 URL(受长度限制、易泄露),POST 参数在 Body(支持更大数据、更多编码类型);
- 浏览器行为:GET 可书签、可后退刷新,POST 刷新会触发重复提交警告。
注意:HTTP 协议本身不限制 GET 带 Body 或 POST 带 URL 参数,但违背语义会导致兼容性问题。"
追问 2:“GET 是幂等的,POST 不是幂等的,这是什么意思?”
低分回答:“GET 执行多次结果一样,POST 执行多次结果不一样。”(没有解释副作用)
高分回答:
"幂等性是指多次执行相同请求,服务器资源状态是否一致,不是指返回结果是否相同。
- GET 是幂等的:无论调用 1 次还是 100 次,服务器资源状态不变(只读取)。即使返回结果不同(如
GET /news返回最新新闻),也是幂等的,因为资源本身未被修改。 - POST 是非幂等的:
POST /orders每次调用都会创建一个新订单,资源状态改变。如果网络超时后自动重试,可能导致重复创建。
幂等性的工程价值:幂等方法可安全重试、可缓存、可预加载。POST 的非幂等性要求我们在分布式系统中设计幂等键等机制防止重复提交。"
- GET 是幂等的:无论调用 1 次还是 100 次,服务器资源状态不变(只读取)。即使返回结果不同(如
追问 3:“GET 请求能带 Body 吗?POST 请求能不带 Body 吗?”
高分回答:
"技术上都可以,但违背语义规范:
- GET 带 Body:RFC 7231 不禁止,但大多数服务器、代理、库不支持。Elasticsearch 等少数场景使用 GET + Body 传递复杂查询条件。
- POST 不带 Body:完全可以,如
POST /logout仅触发登出动作,无需数据。 - POST 带 URL 参数:常见做法,如
POST /orders?source=app标识来源。
最佳实践是遵循语义:GET 用 URL 参数,POST 用 Body。"
追问 4:“POST 产生 2 个 TCP 数据包,GET 产生 1 个,这个说法对吗?”
高分回答:
“这个说法不完全正确。GET 和 POST 的 TCP 数据包数量没有本质区别,都取决于数据量和 TCP 分段。
POST 产生 2 个包的情况仅在启用Expect: 100-continue时出现:客户端先发送 Header,服务器响应100 Continue,客户端再发送 Body。但大多数 POST 请求不启用这个机制,直接在一个 TCP 报文中发送 Header + Body。
反过来,GET 的 URL 如果很长(如大量查询参数),同样会被拆分为多个 TCP 报文。
所以’POST 2 个包、GET 1 个包’是特定条件下的现象,不是普遍规律。”追问 5:“如何防止 POST 请求的重复提交?”
高分回答:
"防止 POST 重复提交需要多层防御:
- 前端层:按钮防抖(点击后禁用 N 秒),但不可靠(F5 刷新可绕过);
- Token 层:页面加载时获取唯一 Token,提交时校验,Token 只能使用一次;
- 幂等键层:客户端生成 UUID 作为
X-Idempotency-Key,服务端用 Redis 缓存’键 → 结果’,重复请求直接返回缓存; - 数据库层:唯一索引兜底(如订单号唯一),重复插入报错。
推荐组合:前端防抖 + 幂等键 + 数据库唯一索引,三层防御确保万无一失。"
追问 6:“什么情况下 GET 和 POST 都不安全?HTTPS 能完全解决吗?”
高分回答:
"HTTP 协议下,GET 和 POST 都是明文传输,都不安全。GET 的参数在 URL 中,更容易通过浏览器历史、服务器日志、Referer 泄露;POST 的参数在 Body 中,相对隐蔽,但无 HTTPS 时仍可被中间人窃听。
HTTPS 能解决传输层的安全问题(加密、完整性、认证),但无法解决:- 应用层漏洞:如 SQL 注入、XSS,与 GET/POST 无关;
- 服务器被入侵:HTTPS 只保证’与真实服务器通信’,不保证’服务器是诚实的’;
- 敏感信息放 URL:即使 HTTPS,URL 参数仍可能出现在服务器日志、浏览器历史中。
所以敏感操作(如支付、密码修改)必须用 POST + HTTPS,且敏感数据绝不放 URL。"
7. 方案选型速查表
| 场景 | 推荐方法 | 核心理由 |
|---|---|---|
| 获取资源/查询数据 | GET | 安全、幂等、可缓存 |
| 创建新资源 | POST | 非幂等,每次创建不同资源 |
| 全量更新资源 | PUT | 幂等,多次更新结果一致 |
| 部分更新资源 | PATCH | 视实现是否幂等 |
| 删除资源 | DELETE | 幂等,删除已删除资源无影响 |
| 提交表单(登录、注册) | POST | 非安全、数据在 Body |
| 文件上传 | POST + multipart/form-data | 支持二进制大文件 |
| 复杂查询条件 | POST + JSON Body | 条件复杂,URL 无法表达 |
| 触发动作(发送邮件、导出) | POST | 非安全、有副作用 |
💡面试官想要的满分总结:
GET 和 POST 的区别不是"参数在 URL 还是 Body",而是语义上的获取 vs 提交。这个语义差异是 RFC 7231 的规范定义,衍生出安全性、幂等性、缓存、浏览器行为等一系列技术差异。
安全性指是否修改资源状态(GET 安全,POST 非安全),幂等性指多次执行效果是否一致(GET 幂等,POST 非幂等)。这两个属性是 RESTful API 设计的基石,决定了方法能否被缓存、能否被安全重试。
技术细节上,GET 可以带 Body(不推荐),POST 可以不带 Body(完全可以),"POST 产生 2 个 TCP 包"仅在
Expect: 100-continue时成立。HTTP 协议本身对 URL 长度和 Body 大小没有限制,限制来自浏览器和服务器配置。生产环境中,POST 的非幂等性是最大的工程挑战。必须通过幂等键(Idempotency Key)+Redis 缓存+数据库唯一索引多层防御,防止网络超时重试导致的重复提交。同时,绝对禁止用 GET 执行删除/转账等敏感操作,避免浏览器预加载和 CSRF 攻击。
最后记住:GET 和 POST 的选择首先看语义,其次看技术约束。语义对了,技术细节自然对;语义错了,技术再完美也是错的。
觉得对您有帮助,麻烦点点关注啦,您的关注是我创作的最大动力~ 🎯