HandyTools Hub

← 全部指南

HTTP 方法详解:GET、POST、PUT、PATCH、DELETE 与真正重要的语义

2026-08-17

每一个 HTTP 请求都是一个动词加一个名词:作用于某个资源方法。多数开发者知道 GET 拉取、POST 提交——然后就在这里停住了,而痛苦恰恰从这里开始。一个会删数据的 GET 请求、一个只改了半条记录的 PUT 请求、一个网络重试后给客户扣了两次款的 POST——每一个都是方法语义的 bug,而只要你真正理解 HTTP 规范承诺了什么,这些都能避免。这篇文章讲清各个方法、决定它们行为的三个属性,以及它们如何与状态码配对。

HTTP 方法到底在表达什么

GET /users/42 HTTP/1.1 这样的请求行,是向服务器做出的一份关于你诉求的承诺。URL 指名哪个资源;方法指名你想对它执行什么操作。整个设计就建立在两者的分离上:同一个 URL 在不同方法下有不同含义,所以单个 /articles/7 端点就能对同一个对象执行读取、整体替换、局部修改和删除。

RFC 9110 定义了三个属性,后面的一切都受它们支配,几乎所有方法 bug 都源于把它们搞混:

  • 安全(Safe)——请求不会改变服务器状态。安全方法可以被爬虫、链接预览、预取引擎放心执行。
  • 幂等(Idempotent)——重复执行 N 次产生的服务器状态与执行一次相同。幂等是重试安全的前提。
  • 可缓存(Cacheable)——响应可以被缓存(浏览器、CDN、反向代理)保存并复用。

把每个方法对应哪个属性背下来,大部分设计难题就迎刃而解了。

你每天都会用到的五个方法

GET——读取资源。 安全、幂等、可缓存。它应该返回一个表示,不改变任何东西。一旦你把变更塞进 GET 端点——一个 ?action=delete 查询参数、一个计数器自增、一个设置 cookie 的登录——你就造出了一个会破坏所有缓存、预取和爬虫的请求。如果某个 URL 有副作用,它就不是 GET。

POST——创建资源,或触发一个流程。 不安全、不幂等,只在显式条件下可缓存。POST 是默认的”做点什么”方法:建记录、提交表单、追加日志行、启动任务。因为服务器每次可能都会创建资源,两个相同的 POST 合法地产生两条记录——所以支付表单和下单按钮需要在上层加幂等键或去重。

PUT——整体替换一个资源。 幂等。PUT /articles/7 的意思是”让 /articles/7 的状态与这个请求体完全一致”。发两次,第二次对状态就是空操作。反面是 PUT 不是局部更新:如果你的 PUT 处理器只设置请求体里出现的字段,你其实是把 PATCH 穿上了 PUT 的衣服——两个客户端更新不同字段时会互相覆盖而不自知。

PATCH——应用一个局部变更。 既不安全也不保证幂等。请求体描述的是变更而不是目标状态——{"title": "new"} 只更新一个字段。像”把浏览量加一”这样的补丁是真正非幂等的:应用两次就加两次。需要重试安全时,用条件头(If-Match 加 ETag)让服务器能拒绝过期或重放的请求。

DELETE——删除一个资源。 幂等。值得注意的怪癖:对已删除资源再次 DELETE 仍然算成功。URL 从来不存在才返回 404 Not Found;资源存在过、现在已经没了,返回 204 No Content(或 200)即可。很多实现每次删除都返回 404,这恰恰破坏了重试循环。

配角:HEAD、OPTIONS、CONNECT、TRACE

  • HEAD——与 GET 完全一样,但响应没有响应体。它是你在不下载的前提下检查资源是否存在、多大、什么类型的方式。可缓存、安全、幂等。
  • OPTIONS——问服务器”这里允许我做什么?“响应的 Allow 头列出支持的方法。CORS 预检大量用到它。
  • CONNECT——建立隧道(通常是通过代理的 TLS)。平时几乎用不到。
  • TRACE——把请求原样回显,让你看到中间节点改了些什么。生产服务器常常禁用它,因为它可能泄露敏感请求头。

安全 vs 幂等 vs 可缓存:对照表

方法安全幂等可缓存
GET
HEAD
OPTIONS
TRACE
PUT
DELETE
PATCH⚠️(不保证)
POST⚠️(仅显式新鲜度时)

这个规律值得内化:安全方法是幂等方法的子集,外加 OPTIONS/TRACE。可缓存安全绑定——因为缓存只有重放响应没有副作用时才能安全复用。这就是”一律用 POST”会悄悄毁掉缓存命中率的原因。

方法与状态码如何配对

方法告诉你想要什么,状态码告诉你结果如何。常见配对:

  • 201 Created——POST(或创建了资源的 PUT)成功;Location 头通常指向新资源。
  • 204 No Content——成功但无需返回内容(DELETE,或返回空体的 PUT)。
  • 202 Accepted——请求被接受做异步处理;资源还没就绪。
  • 405 Method Not Allowed——URL 存在但这个方法不支持;Allow 头列出支持的方法。
  • 409 Conflict——请求与当前状态冲突(例如 If-Match 校验失败的并发 PATCH)。
  • 501 Not Implemented——服务器根本不支持这个方法(比如从不实现 CONNECT 的服务器)。

做配对时的一个心法:201/204/202 是三种”成功了”的回答,选哪个取决于——新建了一个东西、没有东西需要返回、还是任务还在跑。 完整的排障拆解见 HTTP 状态码指南,或者用 HTTP 状态码速查工具查任意一个码。

常见错误(与修法)

  • GET 带副作用。 一个 GET /logout,或一个自增计数的 GET。修法:让变更走 POST。预取、爬虫、链接预览会不请自来地触发它。
  • 所有变更都用 POST。 最容易养成的习惯,但放弃了幂等和可缓存。修法:整体替换用 PUT,删除用 DELETE,POST 只留给”创建新对象”和”运行流程”。
  • PUT 表现得像 PATCH。 只发送变更字段,而 PUT 处理器去合并它们。修法:要么让 PUT 承担整体替换语义(要求完整表示),要么局部更新改用 PATCH。
  • 非幂等的 DELETE。 对”已删除”返回 404。修法:把”已不存在”当作成功,保证重试安全。
  • POST 重试不去重。 下单表单、支付回调、创建调用在网络抖动时都会触发两次。修法:接收幂等键头并拒绝重复,或让创建本身幂等(比如对客户端提供的 ID 加唯一约束)。
  • 无视 Allow 客户端拿到 405 时,服务器的 Allow 头就是契约;按它来,而不是瞎猜方法名。

实用建议

  • 用你掌控的工具测试方法语义——curl是发送任意方法、查看确切状态码和头最快的方式。
  • 只需要存在性或大小的时候,先 HEAD 再大 GET。
  • 设计 API 时让 PUT 和 DELETE 可安全重试;POST/PATCH 默认不可重试,除非你额外加保护。
  • 记住浏览器的 <form> 只支持 GET 和 POST——其余都要靠 fetch 或客户端库(这也是大量老后端被迫把所有操作塞进 POST 的原因)。
  • 调试时永远问自己两个问题:我承诺了什么(方法语义)?服务器同意了吗(状态码)?

速查表

  • 请求 = 方法 + URL。GET 读、POST 建/处理、PUT 整体替换、PATCH 局部改、DELETE 删、HEAD 免体检查、OPTIONS 问权限。
  • 安全:GET、HEAD、OPTIONS、TRACE。幂等:以上再加 PUT、DELETE。可缓存:GET、HEAD(默认)。
  • PUT = 整体替换、幂等;PATCH = 局部变更、不保证幂等。
  • 删除不存在的资源仍是成功——别对”已删除”返回 404。
  • 201 Created / 204 No Content / 202 Accepted = 三种成功;405 + Allow = 方法用错了 URL。
  • 只有幂等方法能安全重试——你的 POST 要去重。