【从0开始学习计算机网络】| HTTP方法疑点解析

🌈个人主页 :一条泥憨鱼 (欢迎各位大佬莅临)

🎬精选专栏传送门:

❄️ 《数据结构》 ❄️ 《AI与Agent那些事》

❄️ 《从0开始学计算机网络》 ❄️

前言:

一团队接过一个线上事故:某核心接口的 QPS 突然暴跌,监控面板上一片飘红。查了半天,发现是前端同学把所有请求都改成了 POST------理由是"POST 能传的参数更多,还不会把数据暴露在 URL 里"。

结果呢?缓存层全部失效,因为 CDN 和浏览器根本不缓存 POST 请求。数据库被读请求打到冒烟,接口响应时间从 20ms 飙到 3s。

这个事故让我意识到:很多写了几年代码的人,其实并没有真正理解 HTTP 方法。今天就把这事掰开揉碎了讲清楚。

先搞清楚 HTTP 方法到底在干嘛

很多人把 HTTP 方法理解成"数据放在哪"------GET 放 URL,POST 放 body。这完全跑偏了。

HTTP 方法本质上是一个语义声明:它告诉服务器和中间件(缓存、代理、网关),你想对某个资源做什么操作。

拿快递打个比方。快递单上有个"操作类型"栏,写着"签收"还是"退回"。这个字段决定了快递员怎么处理这个包裹,而不是决定包裹里装了什么。

同理,HTTP 方法告诉服务器:

  • GET:我想这个资源

  • POST:我想创建一个资源

  • PUT:我想整体替换这个资源

  • DELETE:我想删掉这个资源

这个语义一旦被忽略,整个 HTTP 生态的协作机制就乱了------缓存不知道能否复用响应,网关不知道能否安全重试,服务器不知道请求是否幂等。

六个常用方法逐个拆解

日常开发最常见的就这六个。我把它们分成两组:查和改

查:GET 和 HEAD

GET 是最常用的。它的语义是"获取资源"。特点:

  • 幂等:发 100 次和发 1 次,结果一样,资源状态不变

  • 可缓存:响应可以被浏览器、CDN 缓存

  • 参数在 URL 上(query string)

python 复制代码
# 获取用户列表,page=1 表示第一页

resp = requests.get("https://api.example.com/users?page=1")

HEAD 和 GET 几乎一样,唯一的区别是服务器不返回响应体,只返回响应头。适合用来"探路":

python 复制代码
# 检查文件是否存在、大小多少,不用下载整个文件

resp = requests.head("https://cdn.example.com/bigfile.zip")

print(resp.headers["Content-Length"])  # 文件大小

改:POST、PUT、DELETE

POST 的语义是"在服务器上创建一个新资源"。它不幂等------发两次就创建两个。

python 复制代码
# 创建新用户,每次调用都会生成一个新用户

resp = requests.post("https://api.example.com/users", json={

    "name": "张三",

    "email": "zhangsan@example.com"

})

PUT 的语义是"用请求体**整体替换**指定资源"。它幂等------发多少次,最终资源状态都一样。

python 复制代码
# 整体更新用户 ID=123 的信息,如果不存在就创建

resp = requests.put("https://api.example.com/users/123", json={

    "name": "李四",

    "email": "lisi@example.com"

})

DELETE 的语义是"删除指定资源"。它幂等------删一次和删十次,最终结果都是"没了"。

python 复制代码
# 删除用户 ID=123

resp = requests.delete("https://api.example.com/users/123")

探路:OPTIONS

OPTIONS 用来查询服务器支持哪些方法。这在 CORS 跨域请求里用得最多------浏览器在发正式请求之前,会先发一个 OPTIONS 预检请求。

python 复制代码
# 查看 /users 端点支持哪些 HTTP 方法

resp = requests.options("https://api.example.com/users")

print(resp.headers.get("Allow"))

# 输出类似:GET, POST, PUT, DELETE, OPTIONS

GET 和 POST 的本质区别

这是今天的重头戏。网上很多文章讲"GET 参数在 URL,POST 参数在 body",这没错,但只是表象。

本质区别有三个维度。

维度一:语义------查 vs 改

GET 语义是"查询",POST 语义是"创建(或触发某个操作)"。

这个区别决定了服务器怎么处理请求。GET 请求不应该改变服务器上的任何数据------不该创建文件、不该写数据库、不该改状态。

你见过有人把"删除用户"做成 GET 请求吗?有,而且不少。这类接口被爬虫爬到、被预加载扫到,数据就没了。

维度二:幂等性

这是最容易被忽略、但影响最深远的区别。

  • GET 幂等:重试任意多次都安全

  • POST 不幂等:重试会导致重复创建

幂等性在网络重试 场景下至关重要。客户端发请求超时了,它不知道服务器到底处理了没有。如果请求是 GET,它可以放心重试;如果是 POST,重试可能导致重复下单、重复扣款

所以支付接口通常要求客户端生成一个唯一的 `request_id`,服务端靠它来去重。这就是在给 POST 补幂等性。

维度三:缓存机制

  • GET 可以被浏览器、CDN、反向代理缓存

  • POST 默认不缓存(HTTP 规范也明确要求)

这就是开头那个事故的根源。前端把所有请求改成 POST,等于主动关掉了整个链路的缓存能力。每次请求都穿透到源站,数据库当然扛不住。

实际开发中的踩坑建议

说了这么多理论,落到实操上,有几个原则值得记住。

按语义选方法,别按"参数放哪"选

很多团队图省事,把所有写操作都做成 POST。短期的确方便,但长期来看,接口的语义模糊了,调用方只能靠文档猜测这个接口是否幂等、能否重试。

别用 GET 干改的活

有人为了省事,用 GET 传 `?action=delete&id=123`。这是定时炸弹。浏览器预加载、爬虫、搜索引擎都可能触发这个请求。

POST 和 PUT 别混用

  • 创建用 POST:客户端不知道资源 ID

  • 整体更新用 PUT:客户端知道资源 ID

如果你把更新接口设计成 POST `/users/123/update`,语义上就矛盾了------POST 是创建,但你明明在更新。

幂等性要显式设计

不是所有 POST 都能天然幂等,但你可以通过 `request_id` 去重来"补课":

python 复制代码
def create_order(request_id: str, order_data: dict):

    # 先查 request_id 是否处理过

    if redis.exists(f"req:{request_id}"):

        return {"status": "duplicate"}

   

    # 处理业务逻辑

    order = create_order_in_db(order_data)

   

    # 标记 request_id 已处理

    redis.set(f"req:{request_id}", order.id, ex=3600)

    return {"status": "ok", "order_id": order.id}

怎么选?一张表搞定

回到开头那个事故。如果当时团队能按下面的决策表来选,缓存就不会全挂。

|---------|-----------|----|-----|
| 操作类型 | 方法 | 幂等 | 可缓存 |
| 查询资源 | GET | ✅ | ✅ |
| 只拿响应头 | HEAD | ✅ | ✅ |
| 创建新资源 | POST | ❌ | ❌ |
| 整体替换资源 | PUT | ✅ | ❌ |
| 删除资源 | DELETE | ✅ | ❌ |
| 查询支持的方法 | OPINTIONS | ✅ | ❌ |

简单的决策逻辑:

  1. 东西 → GET

  2. 创建新东西 → POST

  3. 整体替换已有东西 → PUT(知道资源 ID)

  4. 东西 → DELETE

  5. 只需要响应头 → HEAD

  6. 跨域调试 → OPTIONS

结语

HTTP 方法不是约定俗成的"习惯",而是整个 Web 生态协作的基础契约。尊重这套语义,你的接口才能正确享受缓存、重试、安全防护等基础设施的能力。不尊重它,迟早要付出代价------可能是缓存全挂,也可能是数据被爬虫误删。

如果你之前没有认真想过这些方法之间的区别,希望这篇文章能帮你少踩几个坑。下次设计接口时,先问自己一句:这个操作,语义上到底是"查"还是"改"?答案会帮你做出正确的选择。

相关推荐
外滩运维专家10 小时前
HTTPS 证书报错排查手册:6 个高频错误码及解决方法
网络协议·http·https
微学AI10 小时前
一根针指向所有方向:挂谷猜想对 LLM Agent 技能-记忆架构的启示
开发语言·人工智能·架构·挂谷猜想
豆瓣鸡11 小时前
算法日记 - Day3
java·开发语言·算法
纵有疾風起12 小时前
计算机网络的性能指标体系:带宽、时延、吞吐量
计算机网络·rtt·408·带宽·性能指标·吞吐量·时延
韭菜炒鸡肝天12 小时前
VTK开发笔记(一):VTK介绍,Qt..+VSx+VTK.编译
开发语言·笔记·qt
Aaron - Wistron12 小时前
Web API C# (Furion版)带 单元测试
开发语言·后端·c#
Dxy123931021613 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
鲜花飘飘扬14 小时前
HTTP请求头中表示代理IP地址的属性及获取情况
网络协议·tcp/ip·http
05664614 小时前
Python康复训练——常用标准库
开发语言·python·学习