HTTP方法的深度探源与RESTful API的设计思想

即使写程序很多年的老鸟,能真正说清楚道明白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)

  1. 安全(Safe):
    指调用该方法不会修改服务器上的数据(只读操作),直观理解就是不管调用多少次,数据库里的数据都不会被新增、修改或删除。
    Safe 方法的语义本质上是只读的,客户端不请求、也不期望服务器因为这个请求而发生状态变化,Safe ≠ Security ,更贴切的中文可以理解成无副作用请求,是安全方法。
  2. 幂等(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=tom

GET 的特点是: 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,提前判断本地磁盘空间是否足够。

校验文件修改: 检查 ETagLast-Modified,对比本地已下载的文件片段,判断服务器上的文件是否被更新过。

测试链接的有效性(死链检测)

    搜索引擎爬虫或网站链接检查器在校验成千上万个外链时,如果用 GET 请求,会把网页的 HTML、图片全下载下来,极度浪费流量和 CPU。使用 HEAD 请求,只需几百字节的 Header 就能确认目标页面是 200404 还是 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)的沟通与对接成本。