面试官:说说GET和POST的区别?答“URL与Body”的直接回家等通知

45 阅读8分钟

前言:一道经典的“送命题”

在互联网大厂的面试中,HTTP 基础几乎是必考题。而 "GET 和 POST 有什么区别?" 这道题出现的频率之高,堪称“面试界的 Hello World”。

然而,作为面试官,我听过太多60分的回答:

“GET 请求参数在 URL 里,POST 请求参数在 Body 里。”
“GET 不安全,POST 安全。”
“GET 长度有限制,POST 没有。”

如果你的理解仅止步于此,对不起,这只能说明你还在“使用工具”的层面,而非“理解原理”。

今天,我们不仅仅是背八股文,而是要从 RFC 规范、TCP 数据流、浏览器行为到现代架构设计,对这道题进行一次“降维打击”式的拆解。


第一层维度:RFC 7231 规范与语义(道)

我们要做的第一件事,是回归源头。HTTP 协议是由 RFC 定义的,RFC 7231 明确了方法的语义。区别的核心在于两个关键词:Safe(安全性)  和 Idempotent(幂等性)

首先说明一下RFC是什么

RFC(Request for Comments)  是互联网工程任务组(IETF)发布的一系列技术文档的统称,是互联网技术标准和协议的核心载体

1. 安全性(Safe)

这里的“安全”不是指“不泄露隐私”,而是指**“不修改服务器状态”**。

  • GET 是安全的:它不仅用于获取资源,而且被设计为只读操作。无论你调用多少次,数据库里的数据都不应该发生变化。
  • POST 是不安全的:它用于提交数据,通常会引起服务器状态的变更(如创建订单、修改余额)。

2. 幂等性(Idempotent)

这是分布式系统中最关键的概念之一。

  • GET 是幂等的:对同一个 URL 发起一次请求和发起一万次请求,对服务器资源的影响是一样的(不仅结果一样,副作用也一样为零)。
  • POST 是非幂等的:发送一次请求,创建一个资源;发送两次,就创建两个资源。

灾难案例:为什么不能用 GET 转账?

假设你设计了一个银行接口:GET /transfer?amount=1000&to=zhangsan。
如果搜索引擎爬虫(Googlebot)爬取了这个链接,或者用户浏览器的“预加载”(Prefetch)功能自动请求了这个链接,甚至仅仅是用户手抖刷新了一下页面——恭喜你,钱被扣光了。 没有开发者想写出这样的,所以这才是面试官问你的核心,你对于这种微小细节的考察

** 面试官独白:**
“我问这个问题的初衷,是考察候选人是否有系统设计的严谨性。如果连幂等性都不知道,我不放心让你去写支付微服务。”


第二层维度:底层传输与 TCP(术)

这里有一个流传甚广的谣言,很多所谓的“面经”都在瞎教:

 “GET 产生一个 TCP 数据包;POST 产生两个 TCP 数据包。”

辟谣:TCP 层面的真相

HTTP 协议从来没有规定 POST 必须要分两次发送。这个说法主要源于某些早期浏览器或库(如老版本 libcurl)在发送 POST 请求时,会先发送 Header,包含 Expect: 100-continue,询问服务器是否接受数据,收到 100 响应后再发送 Body。

但在现代网络环境下:

  1. 协议层面:  HTTP GET 和 POST 在底层都是 TCP 连接。对 TCP 来说,它根本不知道什么是 GET 什么是 POST,它看到的只是一串字节流。
  2. 实现层面:  现代浏览器(Chrome、Firefox)通常会将 Header 和 Body 尽可能合并在一个 TCP 包中发送(除非 Body 特别大,受到 TCP MSS 或 Nagle 算法影响拆包)。
  3. 性能差异:  所谓“两次包”带来的延迟在长连接(Keep-Alive)和现代高带宽网络下几乎可以忽略不计。

结论:  GET 和 POST 在 TCP 层面没有本质区别,只有报文格式的差异。

Http

// GET 报文
GET /search?q=abc HTTP/1.1
Host: www.example.com

// POST 报文
POST /search HTTP/1.1
Host: www.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 5

q=abc

面试官独白
“能解释清楚 Expect: 100-continue 机制,并且敢于指出网上教程错误的人,通常具备独立思考阅读源码/抓包验证的能力。”


第三层维度:浏览器行为与工程限制(实战)

既然底层一样,为什么工程实践中限制这么多?这更多是浏览器服务器软件的“锅”。

1. URL 长度限制的真相

HTTP 协议从未限制 URL 的长度。
但是,IE 浏览器限制 2083 字符,Chrome 限制约 8KB;服务器端 Nginx 默认限制 header 大小为 4KB-8KB。
后果:  如果你非要用 GET 传输 5KB 的 JSON 配置,可能会触发 HTTP 414 URI Too Long 错误,或者被中间件(CDN/WAF)截断。

2. 缓存与书签

  • GET:  天生对缓存友好。浏览器会主动缓存 GET 请求(若 header 允许),可以加入书签,可以回退(Back)。
  • POST:  默认不被缓存,无法加入书签。
  • 刷新页面(F5):  这是最直观的区别。刷新 GET 页面无事发生;刷新 POST 页面,浏览器会弹窗警告“确认重新提交表单? ”。这是浏览器在保护用户避免重复执行非幂等操作(如重复下单)。

3. 数据编码

  • GET:  只能进行 URL 编码(ASCII)。二进制数据(图片/文件)需要先 Base64 编码,但这会增加体积且不仅容易超长。
  • POST:  支持多种编码,天然支持 multipart/form-data,是上传文件的唯一选择。

第四层维度:安全性与攻击面(防御)

很多人说“POST 比 GET 安全”,理由是“参数在 Body 里看不见”。
这是大错特错!  在 HTTP 明文传输下,Wireshark 抓个包,Body 里的密码和 URL 里的密码一样是裸奔。真正的区别在于攻击面日志

1. 日志泄露(Log Leakage)—— 致命伤

这是工程上最严重的隐患。

  • URL(GET):  会被记录在 Web Server 的 Access Log(如 Nginx 日志)、代理服务器日志、CDN 日志、浏览器的历史记录中。
  • Body(POST):  通常不会被默认记录在服务器日志中。

场景:  如果你用 GET 提交 password=123456,这串密码将永久保存在运维人员管理的 Nginx 日志里。这是严重的安全事故。

2. CSRF(跨站请求伪造)

  • GET:  极易被利用。攻击者只需要在钓鱼网站放一个 ,受害者一打开页面,钱就没了。
  • POST:  稍微麻烦一点,攻击者需要构造一个隐藏表单并用 JS 自动提交。虽然 POST 不能天然防 CSRF(还需要 Token 或 SameSite Cookie),但至少堵住了 

第五层维度:现代架构视角(进阶)

在 RESTful 盛行的今天,我们强调“动词(Method)”对应“操作”。但在微服务和某些现代架构中,规则正在被打破。

1. GraphQL 与 gRPC-Web

如果你使用 GraphQL,你会发现所有的查询(Query)和修改(Mutation)默认都是 POST 请求

  • 为什么?  因为 GraphQL 的查询语句结构复杂,放在 URL 里太长且不美观。
  • 违背 REST 吗?  是的。但这是一种架构权衡。它们不再通过 HTTP Method 区分语义,而是定义了自己的应用层协议语义。

2. CORS 预检(Preflight)

  • 简单请求(Simple Request):  某些标准的 GET/POST 请求直接发送。
  • 复杂请求:  如果 POST 请求携带了 application/json 或自定义 Header,浏览器会先发送一个 OPTIONS 请求(预检),询问服务器“我能发这个 POST 吗?”。这增加了一次 RTT(往返时间)。

总结:面试满分回答路线图

如果面试官再问你这个问题,请把下面这张图印在脑海里,从下往上输出:

维度GETPOST核心关键词
5. 架构GraphQL 全用 POST;CORS 可能触发 OPTIONS适合 RPC 模式权衡
4. 安全参数暴露在日志/历史记录;易受 CSRF 攻击相对隐蔽;日志不记录 Body隐私保护
3. 浏览器可缓存、可书签、刷新无害;受 URL 长度限制不可缓存、刷新报警告;无长度限制用户体验
2. 传输报文载体不同;TCP 层面无本质区别可能存在 Expect: 100 机制TCP流
1. 规范幂等安全(只读)非幂等、修改状态语义 (Semantics)

最后,你可以这样收尾:
“所以,GET 和 POST 的区别不仅仅是参数位置的不同,更体现了我们对 协议语义的尊重、对 数据一致性的保障(幂等)、对 隐私安全的敏感度(日志)以及对 工程约束的权衡。”

这,才是一个 P7 工程师该有的回答。


求点赞 👍
觉得有收获?点赞、收藏、关注,下期我们深挖 HTTPS 握手过程中的性能损耗与优化