即使写程序很多年的老鸟,能真正说清楚道明白HTTP方法(HTTP Verbs / Methods)的也不多,多数都只会使用而不知道所以然。每个人都知道使用GET、POST方法,使用也很熟练,但是底层设计思想并不一定了解。这里,来认真梳理一下HTTP方法背后设计的逻辑。
HTTP Verbs,从词的本意来说,HTTP方法本质上是一组动作,它们是语义化(Semantics )的行为约束。
一. HTTP方法10种
HTTP 标准里常见的方法是这10 个:
| 方法 | 主要作用 |
|---|---|
| GET | 获取资源 |
| POST | 提交数据 / 创建资源 |
| PUT | 完整替换资源 |
| PATCH | 部分修改资源 |
| DELETE | 删除资源 |
| HEAD | 获取响应头,不返回正文 |
| OPTIONS | 查询服务器支持哪些方法/能力 |
| CONNECT | 建立隧道 |
| TRACE | 请求回显,用于诊断 |
| QUERY | 发出搜索请求 |
二.HTTP方法两个核心设计理念:安全(Safe) 与 幂等(Idempotent)
- 安全(Safe):
指调用该方法不会修改服务器上的数据(只读操作),直观理解就是不管调用多少次,数据库里的数据都不会被新增、修改或删除。
Safe 方法的语义本质上是只读的,客户端不请求、也不期望服务器因为这个请求而发生状态变化,Safe ≠ Security,更贴切的中文可以理解成无副作用请求,是安全方法。 - 幂等(Idempotent):
指使用相同的参数执行一次与执行多次,对服务器产生的最终状态/结果是完全相同的。直观理解:f(x) = f(f(x))。比如把某人的余额直接“设置为 100 元”,执行 1 次还是 10 次,余额都是 100 元(幂等);但如果是“把余额加上 100 元”,执行 1 次和 10 次结果截然不同(非幂等)。
| HTTP 方法 | 安全性 (Safe) | 幂等性 (Idempotent) |
| GET | 是 | 是 |
| HEAD | 是 | 是 |
| OPTIONS | 是 | 是 |
| QUERY | 是 | 是 |
| TRACE | 是 | 是 |
| PUT | 否 | 是 |
| DELETE | 否 | 是 |
| POST | 否 | 否 |
| PATCH | 否 | 否* |
可以看出,DELETE是不安全但是是幂等的。幂等的核心含义在于对同一个请求执行一次,与执行多次,其“预期的服务器效果”相同,不是说每次 HTTP Response 必须完全一样。
第一次可能:
204 No Content第二次可能:
404 Not Found但它依然可以是幂等的,因为:
服务器资源最终状态 第一次删除 → 不存在 多次删除 → 仍然不存在RFC 7231 明确指出,幂等性针对的是请求所要求的“预期效果”,响应本身可以不同。
安全性和幂等性规范带来的好处:
—— 遵守安全与幂等约定,才能让 HTTP 缓存(浏览器缓存、Edge/CDN 节点)、反向代理(Nginx 重试机制)发挥最大效能。如果用 GET 去做写操作,预加载引擎或搜索引擎爬虫就可能误删数据。
—— 网络故障下的容错重试策略。
在分布式系统中,如果超时未收到响应:
—— 对于 GET / PUT / DELETE(幂等):客户端或网关可以安全地自动发起重试。
—— 对于 POST(非幂等):不能盲目重试,必须依赖业务层引入防重 Token 或幂等键(Idempotency Key)。
—— API 接口的契约可读性。遵循标准的语义,可以让 API 见名知意,极大地降低前后端协作与外部对接的沟通成本。
三. 10种HTTP方法的用法和特点
1. GET:获取资源
// 给我用户 123 的数据
GET /users/123
// 服务器返回
{
"id": 123,
"name": "Andrew"
}// 带查询条件获取列表
GET /users?page=2&keyword=tomGET 的特点是: GET = 读取,理论上不应该产生修改操作。
2. POST:提交数据,创建资源。
这个用的最多,也最容易理解。
大部分人容易陷入误区(如“GET 参数在 URL,POST 在 Body”或“POST 比 GET 更安全”),但这些只是浏览器实现或应用层的表现,并非 HTTP 规范的本质。
GET 的底层逻辑:
—— 只读契约:客户端表明“我只是想拿数据,不会改变任何东西”。
—— 架构收益:因为安全且幂等,浏览器、CDN、代理服务器可以放心地缓存 GET 请求。这是整个互联网能够承受巨量流量的基础。POST 的底层逻辑:
—— 非幂等与状态变更:客户端表明“我要提交数据,请服务器按照自己的逻辑去处理”。
——架构收益:不强制要求针对特定 URI,处理逻辑高度灵活。由于非幂等,浏览器在刷新的 POST 页面时会弹窗警告(防止重复提交/重复扣款)。
3. PUT:完整更新
假设原来的用户:
{
"id": 123,
"name": "Andrew",
"age": 30,
"email": "a@test.com"
}发送:
PUT /users/123
{
"name": "Tom",
"age": 25,
"email": "tom@test.com"
}语义是:
用新的完整数据替换原来的资源。
旧用户
↓
完全替换
↓
新用户
4. PATCH:局部更新
例如,只改用户名:
PATCH /users/123
{
"name": "Tom"
}意思:
只修改
name,其他字段保持不变。PUT → 全量更新
PATCH → 部分更新
5. DELETE:删除
//删除用户 123
DELETE /users/123
服务器:
204 No Content
或者:
{
"code": 200,
"message": "Deleted successfully"
}6. HEAD 方法的本质就是:“不要主体(Body)的 GET 请求”。
客户端向服务器发送 HEAD 请求时,服务器返回的响应头(Headers)与 GET 请求完全一致,但绝对不能包含任何响应体(Body)。
HEAD 方法的核心价值在于零带宽成本地获取资源的元数据(Metadata)。在真实工程中,它有几个典型应用场景:
① 预先获取大文件信息
HEAD /file.zip可以先判断:文件存在吗? 多大? 什么类型? 有没有更新?
在下载几 GB 的大文件(如系统镜像、游戏包)之前,下载工具会先发一个 HEAD 请求:
校验文件大小: 检查
Content-Length,提前判断本地磁盘空间是否足够。校验文件修改: 检查
ETag或Last-Modified,对比本地已下载的文件片段,判断服务器上的文件是否被更新过。
② 测试链接的有效性(死链检测)
搜索引擎爬虫或网站链接检查器在校验成千上万个外链时,如果用 GET 请求,会把网页的 HTML、图片全下载下来,极度浪费流量和 CPU。使用 HEAD 请求,只需几百字节的 Header 就能确认目标页面是 200、404 还是 301/302 重定向。
③ 检查服务器对断点续传(Range Request)的支持
客户端想并发分多段下载一个文件前,会先发 HEAD 请求查看响应头中是否包含:
Accept-Ranges: bytes如果包含,说明服务器支持分段读取,客户端才可以放心开多线程并行下载。
④ 高频轻量级健康检查(Health Check)
负载均衡器(如 Nginx、L4/L7 网关)或 Kubernetes Kubelet 需要每几秒钟探测一次后端服务是否活着。如果探测使用 GET,后端会不断渲染页面或查询数据库并返回响应体,造成无意义的性能损耗;而 HEAD 请求极轻,能将健康探测对业务系统的干扰降到最低。
7.OPTIONS
OPTIONS /users
意思是:你这个资源支持什么操作?
服务器可能返回:
Allow: GET, POST, OPTIONS它还有一个非常重要的用途:
CORS 预检请求
例如:https://mosang.net,调用 API:https://api.xiaobage.com/,浏览器可能先自动发送:
OPTIONS /users询问服务器:“这个网站是否允许我跨域访问?”
所以后端日志里有时会看到:
OPTIONS /api/users
这通常不是用户直接操作,而是浏览器自动发出的。
8. CONNECT
这个一般普通 Web 开发很少主动使用。
例如访问:HTTPS 网站,代理服务器需要建立隧道:
浏览器
│
CONNECT
↓
代理服务器
│
建立隧道
↓
目标服务器
通常与:HTTPS Proxy 相关。
9. TRACE:会让服务器把请求“反射”回来。
例如:
TRACE /users
服务器把:请求方法、请求头等信息返回。主要用于诊断、调试 HTTP 链路,但因为安全原因,很多服务器直接禁用了它。
10. QUERY
GET 只能把参数放在 URL(Query String)里。但现在的商业系统查询条件极度复杂(如复杂的 JSON 过滤树、长文本特征对比、大数组 ID 查询)。URL 长度是有限制的(通常浏览器/网关限制在 2KB ~ 8KB),超出就会抛出 414 URI Too Long 错误。为了解决 URL 长度限制,大家不得不改用 POST 把复杂查询条件放在 Body 里。但这一改就打破了规矩:
无法被 CDN 缓存: 默认情况下,几乎所有 CDN 和浏览器都拒绝缓存
POST请求。破坏安全性与幂等性: 监控系统和中间件会把这个查询误认为是“数据写操作”,引发误判。
为了解决这个“想用 Body 传复杂查询参数,但又想保持安全与可缓存”的僵局,IETF 提出了全新的 QUERY 方法(最早叫 SEARCH)。
QUERY 方法的官方定义是:Safe(安全的) + Idempotent(幂等的) + 支持 Request Body(有请求体)。
QUERY /products/search HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"category": "electronics",
"price_range": {"min": 100, "max": 500},
"tags": ["wireless", "gaming", "noise-canceling"],
"sort_by": "rating_desc"
}它的核心意义在于:
语义精准: 明确告诉所有中间件“我只是在查数据(安全、幂等),请不要害怕,也不会修改服务端状态”。
容量无界: 查询条件可以任意复杂、任意大,完全放在 Request Body 中,再也不用担心 URL 超长。
重新解锁缓存(关键突破): 因为QUERY明确宣告了自己是安全的,CDN 和网关可以基于URL + Request Body的 Hash 值来重新实现自动化缓存。
三. HTTP方法语义化的本质
这些方法本质是在语义化描述:
这个请求是在读取、创建、替换、修改、删除,还是在查询通信能力。
这能让整个互联网生态更好衔接,例如让服务器优化缓存、让中间件(CDN/代理/网关)能够盲操作,让爬虫与搜索引擎有确切的安全边界,同时标准化语义让接口设计变成了“统一语言”:
GET /articles (获取文章)POST /articles (新建文章)DELETE /articles/1 (删除文章)
统一的语义大幅降低了多端协同(前端、移动端、后端、第三方开放 API)的沟通与对接成本。