跳转至

资源操作 — 五个 HTTP 方法的分工

这一章讲什么: 第 02 章给资源起了名字,这一章给「对资源做什么」定规矩。 核心问题只有一个:你的意图是什么——取?建?整个换?改一处?删? 每个意图对应一个 HTTP 方法,而每个方法自带一套系统性质(能不能安全重试、能不能缓存、会不会撞车)。 选错方法不是风格问题:把该幂等的写成非幂等,重试就会重复下单;让 GET 带了副作用,缓存会把副作用放大一百倍。

1. 顶层全景:五个方法一张表

先解释本章的头号概念。CRUD 是「增删改查」四个字的英文首字母(Create、Read、Update、Delete)——几乎所有管数据的应用,数据的全部一生就是这四件事;REST 的五个方法就是把这四件事翻译成 HTTP 的原生动作1。

  意图        方法     幂等?   可缓存?   载荷要什么          成功码
  ─────────────────────────────────────────────────────────────
  读          GET      ✓       ✓        无                  200 / 404 / 304
  建          POST     ✗       ✗        新资源的内容         201(+Location)
  整个换      PUT      ✓       ✗        资源的完整新版       200
  改几处      PATCH    ✗(不一定) ✗       只要改的字段          200
  删          DELETE   ✓       ✗        无                  204

  图说:这一章就是逐行讲清「为什么这一行长这样」。幂等=重复发同一个请求,结果和发一次一样。

幂等(idempotent,读作「幂等的」)是全章最要紧的一个词,先把定义钉死:一个操作重复多少次,效果和做一次一样,它就是幂等的。 数学里的「幂」就是同一个数自乘多少次;「等」是结果相等。GET 是幂等的——查一百次,数据还是那份数据(前提是不含别人写入);POST 不是——同样的「创建用户」请求发两次,服务器上就多出两个一模一样的用户2。

为什么这个词值得专门立起来?因为网络是会丢的。客户端发出一个请求,没等到响应——它分不清是「请求丢了」还是「响应丢了」。唯一能安全重试的操作,是幂等的那些:重发一次 PUT,服务器再替换一遍,结果不变;重发一次 POST,你就建了两份。整个互联网的重试逻辑、缓存逻辑、代理逻辑,全都建立在「方法自带幂等性承诺」这张表上。这张表就是 HTTP 方法这套约定的全部价值。

本章的主线例子:图书馆里那本 42 号书

后面每一节,我们都用同一个具体输入走完:图书馆 API 里的 /books/42,一本《Advanced Chemistry》,当前状态:

{ "id": 42, "title": "Advanced Chemistry", "author": "Dr. Jane Smith", "genre": "Science" }

(这是原书 PATCH 一节给的起始资源,后面所有改动的每一步,都从这份 JSON 出发。)我们要在这本书上完成五种意图:读它、建新书、整体换它、改它的一个字段、删掉它——每种意图走一个方法,每一步旁边都有具体的请求、状态码和改完后的资源状态。

本章反复用到语义——指一个方法在协议里承诺的行为:GET 承诺只读,PUT 承诺替换——这个词先立住,后面各节都在兑现或违背这些承诺。

2. GET:科学的观察仪器

解决什么问题

GET 的意图只有一个:取数据,不碰数据。书里给它的角色是「公正的观察仪器」:科学家把仪器对准研究对象,仪器可以被校准到极其精密,但测量本身绝不能扰动对象——测一次和测一百次,读数反映的都是对象此刻的状态3。

这个「不扰动」的性质有个正式名字:安全(safe)。安全的直接红利是缓存:既然 GET 不改任何东西,它的响应就可以被存下来复用。原书的天气例子走一遍:

  请求:  GET /weather/US/FL/Pensacola
  响应:  HTTP/1.1 200 OK
         Cache-Control: max-age=3600        ← 服务器说:这份响应可以存 1 小时(3600 秒)
         { "city": "Pensacola", "temperature": {"value": 25, "scale": "Celsius"}, "weather": "Sunny" }

  图说:一小时内客户端再要同一份天气预报,直接读本地缓存,不再惊动服务器。
  三个状态码各有分工:200(在,给你)、404(没有这东西)、304(没改过,继续用你存的那份)。

Cache-Control 是一个 HTTP 头(头就是随请求/响应附带的一行键值对,管元数据),max-age=3600 是它对缓存的指令4。配套的还有 ETag 与 Last-Modified 两个头,负责「缓存里那份还新鲜吗」的校验——ETag 在第 4 节冲突处理里还会回来当主角。

传真的类比:GET 拿的是「副本」,不是「原件」

书里有个别致的类比:GET 和传真机共享同一个原则——非侵入式复制。传真机把文件的一幅像素复本传过电话线,原件留在原地;GET 把资源状态的一张数据快照传过网络,服务器上的资源原封不动。两边都不展示原件,递出的都是「高度像它的东西」。传真机发完不存档,GET 也无状态:每个请求自成一体,服务器不记得你和它交互过5。书里诚实标注:没有直接证据说 Fielding 设计 REST 时参考了传真机——这是帮助理解的平行类比,不是史实6。

两个常见误用(书里点名)

  • GET 里藏副作用。 设计一个「读取时顺手给浏览计数 +1」的 GET,就毁了它的全部性质:缓存层会把响应存下来,计数再也不涨;代理会重复同一个 GET 做预取,计数虚高。安全方法的承诺一旦打破,下游所有自动化都会把你的副作用放大7。
  • 超长查询串。 URL 太长会被中间的设备截断。数据一大,正确做法是换 POST 把数据放进请求体——GET 的查询串只适合短参数8。

缓存为什么天然属于 GET:两个历史片段

书里在 GET 的缓存这件事上花了不少笔墨,并且两次主动声明「这是类比,不是史实」。这两处值得原样接住,因为它们展示了一种读技术史的正确姿势。

第一个片段是日志本。书里把缓存比作科学家的记录本:仪器测过一次的读数记下来,下次直接查本,不必再测——「缓存之于 GET,正如日志本之于观察」;由此 GET 的角色从「规格说明书」上升成了「只读交互的概念框架」7。第二个片段是 FTP 的遗产。FTP(早期文件传输协议)当年就懂得把传过的文件存一份副本、避免重复下载;HTTP 的最小化网络流量机制与之同源,八十年代末九十年代初的协议设计者已经很懂缓存的收益——书里照例标注:没有直接证据说这套历史影响了 Fielding,但概念上的平行关系帮助理解8。

机制层面,GET 的缓存在两端都成立:客户端缓存像浏览器存图片和 CSS 那样,把 API 响应存本地,重复请求秒回;服务器端缓存把易变数据的副本放在离数据源更近的地方,给源站减负。管这两道闸的三个头:ETag(资源的版本指纹)、Cache-Control(存多久)、Last-Modified(最后改动时间)——它们共同决定「哪份本地副本还新鲜、哪份已经 stale(过期变质)」9。

缓存也有反噬,书里点了两样:一是保守缓存的错配——资源已经变了、缓存还没刷新,客户端拿到过期读数,对依赖实时性的动态资源(比如当前气温)这是直接的事故;二是长 URL 与缓存无关但常被一起误用——超长的查询串会被中间设备截断,大数据该走 POST 的请求体10。

选对方法的两样副产品

书里在开头埋了一条容易被略过的线索:方法的语义不只服务通信,还服务治理。因为每个方法的「意图」是显式的,系统可以沿着它划权限的线——只许某些身份 GET、另一些身份才能 PUT/DELETE;还能从日志里统计读写的比例,照着这个画像去做架构优化(书里给的比例见 §4 末)11。换句话说,方法语义是安全的抓手和运维的仪表——把动作都塞进 POST,你不只是违背了风格,还把这两样抓手一起扔了。

3. POST:化学反应式的创建

解决什么问题

POST 是 GET 的反面:GET 只观察,POST 主动改变后端状态——像化学家把试剂混在一起引发反应,每次 POST 都让服务器进入一个新状态。它的本职是创建:在集合下新增一个子资源9。

怎么做:创建一个用户,走一遍

  请求:  POST /users HTTP/1.1
         { "name": "John Doe", "email": "john.doe@example.com" }
  响应:  HTTP/1.1 201 Created
         Location: /users/123
         { "id": 123, "name": "John Doe", "email": "john.doe@example.com" }

  图说:201 Created 是「建好了」的专用码;Location 头告诉客户端新资源在哪个地址。
  之后客户端可以 GET /users/123 访问它——创建和读取的地址第一次在这里接上。

注意这里 POST 指向的是集合 /users——「往用户集里添一个」。这正是第 02 章复数命名的用处:看到 /users 就知道它是集合,集合上 POST 就是添加语义。

非幂等:重试地狱从这里开始

把上面的请求原样重发一次(网络重试、用户连点两下提交),服务器会创建第二个用户——同样的姓名邮箱,不同的 id。这就是非幂等的含义:请求的每一次到达都产生新效果。所以书里立了两条规矩:

  • POST 的响应绝不可缓存。 书里构造了一个灾难剧本:某电商给 POST /orders 的响应开了缓存——「客户端以为下了两单、服务器只处理了一单、库存没扣、钱只收了一份」,平台级数据错乱10。
  • 补救办法:Cache-Control: no-store 头明确告诉所有中间层不许存;给每个请求带唯一 ID,让服务端能认出「这是重试,不是新单」11。

POST 还有一个 GET 没有的本事:扛大载荷。上传一张 2000×2000 像素、4 MB 的高清图,GET 做不到(URL 塞不下),POST 的请求体天生自由——嵌套的 JSON、文件、多段表单都能装。但书里有节制的提醒:1.5 GB 的视频直传会把服务器内存撑爆、挤占慢网用户、撞上默认超时;对策是压缩、分块(chunk,把文件切成小段分批传)、给客户端传进度条12。

过载:POST 不是万能垃圾桶

书里点名的最大误用:拿 POST 干 PUT/PATCH 的活。例子:社交 API 的 POST /user/123/notifications 用来「触发一条通知」——它并不创建新数据,只是触发一个动作。因为 POST 非幂等,客户端一重试,用户就收到两条同样的「你有新粉丝了」。正确做法是把通知建模成用户资源的一个状态,用幂等的 PUT /user/123/notifications 去更新它——重试多少次,效果都是「通知处于已触发状态」13。

4. PUT:整体替换,以及它的两场硬仗

解决什么问题

PUT 的意图是替换:把资源现在的整个状态,换成请求体里描述的下一个状态。和第 02 章那个句子类比呼应:POST 在集合(主语是复数)上添加,PUT 在个体(主语是单数、地址唯一)上替换14。

怎么做:主线例子走第一步

回到 /books/42。规范的 PUT 流程是先读后写:

  ①  GET /books/42
     → { "id": 42, "title": "Advanced Chemistry", "author": "Dr. Jane Smith", "genre": "Science" }

  ②  PUT /books/42 HTTP/1.1
     { "id": 42, "title": "Advanced Chemistry", "author": "Dr. Jane Smith",
       "publisher": "BPP", "genre": "Science" }
     → 200 OK,响应体是替换后的完整资源

  图说:② 比 ① 多了一个 publisher 字段。PUT 的特点是「我发什么,你就是什么」——
  载荷里没有的字段就消失,载荷里新的字段就出现。这里 GET 回来的原样发回,只加一个字段,是 PUT 的标准用法。

「我发什么你就是什么」这句要当真:PUT 的载荷必须是资源的完整表示。发出去的版本字段少,旧版里多出来的字段就被删掉;字段值不同,就被覆盖。所以 PUT 幂等——第二次发同样的 PUT,服务器上已经是那个状态,再替换一遍结果不变15。

硬仗一:upsert 的诱惑

一个诱人的省事写法:PUT 时如果 /books/45 不存在,就顺便创建它(update + insert = upsert)。书里的反对理由值得背下来:它会混淆客户端的意图。客户 A 想「建一本新书」、不知道 45 号已被占,PUT 下去就把别人的书覆盖了;客户 B 想「更新 45 号」,发出的却是同样的请求、收到 201 Created。同一个请求,两种意图、两种结果——幂等性承诺在这里变成陷阱。原书的结论:创建归 POST,更新归 PUT,分开16。

硬仗二:并发冲突——本章最要紧的机制

两个人同时编辑 /books/42,都先 GET、各自改、再都 PUT——后写的把先写的整个吞掉,先写的人那一版改动无声无息地消失了。这叫竞态条件(race condition):结果取决于请求到达的时序,「最后进,下次出」(last in, next out)——书里这个倒装记法,说的是最后到达的请求决定下一个状态17。

解法原书走了完整一遍,这是全章最该动手跟的步骤:

  ①  两个客户端各自 GET /books/42,响应里都带:  ETag: "xyz123"
      (ETag 是资源当前版本的一个指纹标记;内容一变,ETag 就变)

  ②  客户端乙先把它的 PUT 发出去,服务器接受,资源更新,新 ETag 变成 "abc456"

  ③  客户端甲这时才发它的 PUT,带上它的旧指纹:
      PUT /books/42 HTTP/1.1
      If-Match: "xyz123"                ← 「只有当资源还是我拿时的那个版本时,才允许我写」
      { …甲改过的完整内容… }

  ④  服务器比对:请求里的 xyz123 ≠ 当前 abc456 → 拒绝:
      HTTP/1.1 412 Precondition Failed
      { "error": "Precondition Failed", "message": "This book has been modified by another user." }

  图说:If-Match 是一把「乐观锁」——不锁资源(那样吞吐量要遭殃),只在写的那一瞬间
  验明「我基于的版本还是当前版本」。不匹配就 412,客户端甲可以重新 GET、合并、再写。

这套 ETag + If-Match 组合叫条件请求(conditional request),出自 RFC 7232;412 这个状态码的意思是「前提条件不满足」。它把「最后写入赢」换成「冲突被显式报出来」,数据完整性保住了18。

PUT 的两个误用

  • 拿 PUT 做局部更新:只发想改的那个字段,但忘了 PUT 是整体替换——载荷没提的字段全被抹掉。想改一处,该用 PATCH(下节)。
  • PUT 不指具体资源:PUT 必须落在唯一标识的个体地址上;PUT /users(不带 id)等于让服务器猜要换谁,后端状态直接乱掉19。

书里还给了五个方法的流量画像(按公开 API 的一般经验):GET 占 50–60%,POST 20–30%,PUT 10–15%,DELETE 最少,5–10%。含义:客户端并不常整体替换资源——日常修改多是小改动,这正是 PATCH 的用武之地20。

5. PATCH:外科手术式的局部修改

解决什么问题

一本资源有一百个字段、你只想改书名——用 PUT 你得把一百个字段全发回去,其中九十九个是白白重复的网络流量。PATCH(出自 RFC 5789)的意图是只改载荷里点名的字段,其余原封不动。书里给的手术类比:PUT 是换掉整个培养皿里的样本,PATCH 是基因编辑师只动一个基因21。

怎么做:主线例子走完三种修改

还是 /books/42,起始状态同 §1。PATCH 的三种基本动作(来自原书的实现约定:按字段在载荷里出现与否决定动作):

  加字段:  PATCH /books/42   { "language": "English" }
           → 资源 = 原样 + 新增 language 字段(服务器看到载荷里有、当前资源里没有的字段 → 添加)

  改字段:  PATCH /books/42   { "title": "Advanced Chemistry: The Basics" }
           → title 被更新;载荷里只发这一个字段,author/publisher/genre 全部保留

  删字段:  PATCH /books/42   { "genre": null }
           → genre 字段被移除(约定:null 值表示删除)

  三个一起来:PATCH /books/42   { "language": "English", "title": "Advanced Chemistry: The Basics", "genre": null }
           → 一次请求完成加、改、删

  图说:每一步都只动被点名的字段。改完的完整状态每步都能在响应里看到——这正是主线例子的「每一步旁边有具体状态」。

省多少?原书算了账

原书拿这个例子的载荷大小做了个粗略对照(单位:字节)22:

操作 载荷大小
PUT(完整替换) 122
PATCH(加字段) 28
PATCH(改字段) 48
PATCH(删字段) 20

原书的结论:PATCH 平均比整体替换小约 75%,或者说完成同样的改动只需要约三分之一的数据。再叠一层:Gzip 压缩(第 07 章细讲)平均还能把 JSON 压掉约三分之二。资源越大、改动越小,PATCH 越赚——这就是「载荷形状决定 PUT 和 PATCH 谁先谁后」的算术根据。

PATCH 不(天然)幂等:计数器例子

PATCH 的载荷可以不描述「目标状态」,而描述「要执行的操作」。原书的计数器例子:资源 { "id": 101, "name": "Page Views", "count": 50 },发一个指令式补丁:

  PATCH /counters/101   { "operation": "increment", "field": "count", "value": 1 }
  → count 变 51
  原样重发一次 → count 变 52;再发一次 → 53

「加一」这种操作依赖资源的当前状态,重放一次就加一次——所以它不幂等。HTTP 对幂等的定义是「几个相同的请求和单个请求的结果相同」,指令式 PATCH 天生不满足。书里列的 PATCH 误用清单:把 PATCH 当 PUT 发完整载荷(浪费且混淆)、无视冲突管理(改单字段更要 ETag/If-Match,见 §4)、拿它做大规模的更新(逻辑复杂化)、强行把它做成幂等(违背语义)23。

6. DELETE:一锤定音的删除

解决什么问题与怎么做

DELETE 是资源生命周期的最后一幕,意图最单一:抹除。主线例子的收尾:

  DELETE /books/42 HTTP/1.1
  → HTTP/1.1 204 No Content        ← 成功,响应体为空;「204 无内容」是删除成功的专用信号

幂等的证明很妙:删一次,书不存在;原样再删一次,书还是不存在——「不存在」这个状态重复多少次都是同一个状态。所以重试 DELETE 是安全的24。

为什么需要谨慎,边界在哪

DELETE 的效果不可逆(除非有软删除),所以书里立了三条防线:权限(谁都能调 DELETE 是灾难)、确认机制、以及考虑软删除(soft delete)——资源不进坟墓,只是被标记为「已停用、不可见」,还能捞出来25。另外删除可能波及完整性:删了作者,他的书怎么办?这类级联问题不在本章范围,但「DELETE 之前想清楚牵连」是书里语气的核心。

7. 作者的判断与证据

  • 仪器的类比体系是本章的「教学法」而非证据:GET=科学仪器、POST=化学反应、PUT=替换试剂、PATCH=基因编辑、DELETE=丢弃样本。这套类比的作用是让人记住语义差别,原书自己也承认有些类比(传真机)无直接史料证据。它们是好台阶,不是论证26。
  • 有硬数据的部分:PATCH 的字节对照表(Table 3.1)、五方法流量占比(50-60/20-30/10-15/5-10%)——这两个数给出了「PUT 是特例、PATCH 更常用」的经验支撑27。
  • 作者的判断:「方法要按客户端的意图选,让动词的语义(安全、幂等)匹配想要的服务器状态变化」——这是全章的结论句,也是唯一经得起反问的标准28。

8. 边界与局限

  • 指令式 PATCH 的约定不通用。 「null 表示删字段」是原书示例实现的约定,不是标准;业界还有 JSON Patch(RFC 6902,一套正式的「加/删/换/移」操作写法)和 JSON Merge Patch(RFC 7386)两种正式规范,原书只字未提——真到实现时,直接采用一个现成规范比自己发明约定稳妥。
  • 流量占比没有出处。 「GET 占 50–60%」这类数字是行业经验之谈,不是测量;作量级直觉用,别当基准。
  • 条件请求只解决「写冲突」,不解决「读冲突」:两个人同时读再各自写,412 让后写者失败,但「怎么合并两个人的改动」是业务问题,HTTP 不管。
  • DELETE 的幂等有个灰色地带:删的时候返回 204,再删一次返回 404 还是 204?两种实现都存在,原书没有讨论;对外承诺一种并写进文档是实务正解。

9. 可带走的

本章主线例子一行走完: /books/42《Advanced Chemistry》——GET 拿到 {id,title,author,genre};POST /books 建 45 号新书回 201 + Location: /books/45;PUT 把整份 {id,title,author,publisher:"BPP",genre} 发回去整体替换;PATCH 只发 {genre:null} 删掉一个字段(载荷 20 字节,对比 PUT 的 122 字节);DELETE 收 204,书从此不存在。五个意图,五种不可混用的系统性质。

  1. 先问意图再选方法:取=GET、建=POST、整体换=PUT、改几处=PATCH、删=DELETE;「触发动作」优先想办法建模成资源状态更新(幂等),退而求其次才用 POST。
  2. 幂等=重试安全:GET/PUT/DELETE 可以放手重试;POST/指令式 PATCH 重试前先想「再来一次会怎样」。
  3. GET 里永远别有副作用——缓存和代理会把副作用放大到失控。
  4. POST 响应永不缓存;给它配 Cache-Control: no-store 和请求唯一 ID。
  5. PUT 的载荷是完整资源:少发一个字段 = 删掉一个字段。
  6. 并发写必须上 ETag + If-Match:让冲突被 412 显式报出来,而不是被「最后写入赢」悄悄吞掉。
  7. PATCH 的「省」是算术:改得越少、资源越大,PATCH 越值;但记住它的非幂等本性。
  8. upsert 是潘多拉的盒子:创建和更新的意图分开,POST 和 PUT 各司其职。
  9. DELETE 前先问三件事:权限到位了吗?要不要软删除?删了会牵连谁?

10. 原文地图

主题 原书章 原文位置
CRUD 与方法的对应 Resource Manipulation text/10-fm-introduction.txt:35(搜「create, read, update, and delete」)
幂等与 GET 的仪器类比 Resource Manipulation text/10-fm-introduction.txt:38(搜「idempotence」) · :73(搜「impartial observer」)
GET 的状态码与 Cache-Control 例 Resource Manipulation text/10-fm-introduction.txt:76(搜「304 Not Modified」) · :94(搜「max-age=3600」)
传真机类比与无状态 Resource Manipulation text/10-fm-introduction.txt:133(搜「Facsimile」) · :136(搜「stateless」)
GET 误用:副作用与长 URL Resource Manipulation text/10-fm-introduction.txt:154(搜「Common misuses」)
POST 创建与 201 + Location Resource Manipulation text/10-fm-introduction.txt:172(搜「201 Created」) · :208(搜「Location: /users/123」)
POST 非幂等与重复创建 Resource Manipulation text/10-fm-introduction.txt:226(搜「redundant user accounts」)
缓存 POST 响应的灾难剧本 Resource Manipulation text/10-fm-introduction.txt:235(搜「cache POST responses」) · :241(搜「no-store」)
POST 大载荷与分块对策 Resource Manipulation text/10-fm-introduction.txt:229(搜「4 MB」) · :442(搜「1.5 GB」)
POST 过载:触发通知该用 PUT Resource Manipulation text/10-fm-introduction.txt:337(搜「/user/123/notifications」) · :415(搜「As an idempotent action」)
PUT 整体替换与先读后写 Resource Manipulation text/10-fm-introduction.txt:465(搜「replacement re-defines」) · :474(搜「first GETs the current state」)
upsert 陷阱 Resource Manipulation text/10-fm-introduction.txt:564(搜「upsert」)
竞态条件与 last in, next out Resource Manipulation text/10-fm-introduction.txt:570(搜「Race condition」)
ETag + If-Match 全程演示 Resource Manipulation text/10-fm-introduction.txt:583(搜「conditional requests with ETags」) · :601(搜「ETag: "xyz123"」) · :634(搜「If-Match」) · :706(搜「412 Precondition Failed」)
PUT 误用:局部更新与不指资源 Resource Manipulation text/10-fm-introduction.txt:740(搜「partial updates」) · :743(搜「specific resources by their unique identifiers」)
五方法流量占比 Resource Manipulation text/10-fm-introduction.txt:746(搜「50-60%」)
PATCH 的三种字段操作 Resource Manipulation text/10-fm-introduction.txt:758(搜「RFC 5789」) · :788(搜「language」) · :839(搜「update the current」) · :884(搜「genre": null」)
载荷大小对照表 Resource Manipulation text/10-fm-introduction.txt:1032(搜「Table 3.1」) · :1035(搜「75% smaller」)
PATCH 非幂等的计数器例子 Resource Manipulation text/10-fm-introduction.txt:1050(搜「counter」) · :1080(搜「increment」)
DELETE 与 204、软删除 Resource Manipulation text/10-fm-introduction.txt:1157(搜「goes poof」) · :1169(搜「204 No Content」) · :1175(搜「soft delete」)
结论:按意图选方法 Resource Manipulation text/10-fm-introduction.txt:1357(搜「ensuring the HTTP verb」)

  1. 出处:「CHAPTER 3 Resource Manipulation」第 35 段(text/10-fm-introduction.txt:35,搜「create, read, update, and delete」)。原文:「a set of operations called create, read, update, and delete (CRUD), sustain the lifecycle of data... POST creates new resources, GET reads them, PUT updates existing resources, and DELETE clears those that are no longer needed.」 ↩

  2. 出处:「CHAPTER 3 Resource Manipulation」第 38 段(text/10-fm-introduction.txt:38,搜「idempotence」)与第 226 段(text/10-fm-introduction.txt:226,搜「non-idempotent」)。原文对非幂等的解释:「to send this POST request more than once yields redundant user accounts with identical details but different IDs」。第 1047 段给出 HTTP 语义的定义:「Idempotency, as defined in HTTP semantics, requires that several identical requests provide the same outcome as a single request.」 ↩

  3. 出处:「CHAPTER 3 Resource Manipulation」第 73 段(text/10-fm-introduction.txt:73,搜「impartial observer」)。原文:GET 如科学仪器,「If observation passively views a thing, then the process itself should have no side effects on the thing observed.」 ↩

  4. 出处:「CHAPTER 3 Resource Manipulation」第 76 段(text/10-fm-introduction.txt:76,搜「Cache-Control」)与第 79 段(text/10-fm-introduction.txt:79,搜「1 hour」)。三个状态码的分工见第 76 段:「A 200 OK status if the resource is available... a 404 Not Found... A 304 Not Modified response means the resource has not changed since the last request.」 ↩

  5. 出处:「CHAPTER 3 Resource Manipulation」第 133–139 段(text/10-fm-introduction.txt:133,搜「facsimile」;:139,搜「non-invasive replication」)。原文:「Both enable non-invasive replication... the value lies not in the original thing but in the ability to impart a facsimile of the current state of the thing over a medium.」 ↩

  6. 出处:「CHAPTER 3 Resource Manipulation」第 133 段(text/10-fm-introduction.txt:133,搜「little evidence exists directly linking」)。原文:「Although little evidence exists directly linking fax machines to Fielding's work, conceptual parallels... help us to understand a possible link.」 ↩

  7. 出处:「CHAPTER 3 Resource Manipulation」第 127 段(text/10-fm-introduction.txt:127,搜「logbook」)。原文:「A cache works like a scientist's logbook. In storing past measurements, a logbook provides quick access to known data and reduces repeat observations... GET becomes more than a technical specification as a conceptual framework for read-only web interactions.」 ↩↩

  8. 出处:「CHAPTER 3 Resource Manipulation」第 145 段(text/10-fm-introduction.txt:145,搜「File Transfer Protocol」)。原文明确说 FTP 式的缓存机制与 HTTP 的省流量机制相似,且第 133 段已声明此类历史联系「little evidence exists directly linking」——按平行类比读,不按史实读。 ↩↩

  9. 出处:「CHAPTER 3 Resource Manipulation」第 148 段(text/10-fm-introduction.txt:148,搜「ETag, Cache-Control, and Last-Modified」)。原文:「These headers define the rules of a control system that gates if and when data will be retrieved again from the server.」 ↩↩

  10. 出处:「CHAPTER 3 Resource Manipulation」第 151 段(text/10-fm-introduction.txt:151,搜「outdated reading」)与第 157 段(text/10-fm-introduction.txt:157,搜「truncation」)。缓存过期读数的例子是「a GET request for the current temperature may give back an outdated reading」。 ↩↩

  11. 出处:「CHAPTER 3 Resource Manipulation」第 38 段(text/10-fm-introduction.txt:38,搜「access control」)。原文:「By drawing clear boundaries around actions, they guide access control... The differences between the actions HTTP methods express also reveal patterns in long-term system behavior, exposing the ratio between read and write operations」。 ↩↩

  12. 出处:「CHAPTER 3 Resource Manipulation」第 229 段(text/10-fm-introduction.txt:229,搜「2000 x 2000」)与第 442 段(text/10-fm-introduction.txt:442,搜「1.5 GB」)。三种对策(压缩、分块、进度指示)见第 445 段。 ↩

  13. 出处:「CHAPTER 3 Resource Manipulation」第 337 段(text/10-fm-introduction.txt:337,搜「trigger a notification」)与第 376 段(text/10-fm-introduction.txt:376,搜「uses PUT to trigger this action」)。原文:「the main flaw in this approach becomes apparent when a client repeats the request, leading to unintended duplicate notifications」。 ↩

  14. 出处:「CHAPTER 3 Resource Manipulation」第 465 段(text/10-fm-introduction.txt:465,搜「PUT」)。「Used correctly, PUT targets a specific resource or a collection」见第 471 段(text/10-fm-introduction.txt:471,搜「always operates on an existing resource」)。 ↩

  15. 出处:「CHAPTER 3 Resource Manipulation」第 471 段(text/10-fm-introduction.txt:471,搜「idempotent」)与第 558 段(text/10-fm-introduction.txt:558,搜「publisher」)。原文:「Repeating the operation has no real impact on state because the method is idempotent」;例子中单个 publisher 字段被加入,后续 GET 都带它。 ↩

  16. 出处:「CHAPTER 3 Resource Manipulation」第 564 段(text/10-fm-introduction.txt:564,搜「upsert」)。原文:「it may blur the client's intent... PUT should focus squarely on updates. API designers are well advised to separate these two concerns」。 ↩

  17. 出处:「CHAPTER 3 Resource Manipulation」第 570 段(text/10-fm-introduction.txt:570,搜「Race condition」)。原文:「the last request will always win and overwrite the earlier one, potentially losing important changes... the timing of requests determines the next state of a resource: last in, next out.」 ↩

  18. 出处:「CHAPTER 3 Resource Manipulation」第 583 段(text/10-fm-introduction.txt:583,搜「conditional requests with ETags」)、第 601 段(text/10-fm-introduction.txt:601,搜「ETag: "xyz123"」)、第 634 段(text/10-fm-introduction.txt:634,搜「If-Match: "xyz123"」)、第 706 段(text/10-fm-introduction.txt:706,搜「412 Precondition Failed」)。「a robust implementation... ensures that updates are applied only if the resource's state matches the client's copy」见第 734 段。 ↩

  19. 出处:「CHAPTER 3 Resource Manipulation」第 740 段(text/10-fm-introduction.txt:740,搜「partial updates」)与第 743 段(text/10-fm-introduction.txt:743,搜「unique URI」)。原文:「PUT must point at a specific resource... Using PUT without a reference to a specific resource ID leaves a gap in the request.」 ↩

  20. 出处:「CHAPTER 3 Resource Manipulation」第 746 段(text/10-fm-introduction.txt:746,搜「50-60%」)。原文:「GET represents 50-60% of traffic, POST is used 20-30% of the time, PUT comes in third place, at 10-15%, and DELETE... 5-10% of all requests.」 ↩

  21. 出处:「CHAPTER 3 Resource Manipulation」第 752 段(text/10-fm-introduction.txt:752,搜「surgical precision」)。原文:「PATCH makes targeted modifications rather than complete replacements... Like a geneticist tweaking a single gene instead of modifying the entire genome」。 ↩

  22. 出处:「CHAPTER 3 Resource Manipulation」第 1032 段(text/10-fm-introduction.txt:1032,搜「Table 3.1」)与第 1035 段(text/10-fm-introduction.txt:1035,搜「75% smaller」)。原文:「the PATCH method requires payloads that are ~75% smaller than the full, original resource, or 1/3rd the amount of data... Applying Gzip compression to JSON data on average brings the payload size down by 2/3rds」。 ↩

  23. 出处:「CHAPTER 3 Resource Manipulation」第 1050–1128 段(text/10-fm-introduction.txt:1080,搜「operation": "increment"」;:1128,搜「operates on a mutable resource」)。误用清单见第 1131 段(「Send a full representation... Ignore conflict management... bulk updates... Making PATCH idempotent」)。 ↩

  24. 出处:「CHAPTER 3 Resource Manipulation」第 1157 段(text/10-fm-introduction.txt:1157,搜「idempotent」)。原文:「DELETE is idempotent. Issuing the same request multiple times will yield the same result: absence.」响应例见第 1169 段。 ↩

  25. 出处:「CHAPTER 3 Resource Manipulation」第 1175 段(text/10-fm-introduction.txt:1175,搜「soft delete」)。原文:「API designers must consider proper authorization, confirmation, and safeguards... in some cases, a soft delete may be more appropriate」。 ↩

  26. 出处:类比体系散见全章(GET :73、POST :166、PUT :465、PATCH :752、DELETE :1157);「无直接证据」的自白见 :133。 ↩

  27. 出处:「CHAPTER 3 Resource Manipulation」第 1035 段(text/10-fm-introduction.txt:1035,搜「75% smaller」)与第 746 段(text/10-fm-introduction.txt:746,搜「50-60%」)。原文称流量数字基于「general usage in public APIs」。 ↩

  28. 出处:「CHAPTER 3 Resource Manipulation」结论第 1181 段(「We move from rules to reason. We no longer just use HTTP, we speak it.」);按意图选方法的原句在第 1357 段章末第 10 题的答案(text/10-fm-introduction.txt:1357,搜「ensuring the HTTP verb」)。原句:「The method should be selected based on the client's intent, ensuring the HTTP verb's semantics (safe, idempotent, etc.) match the desired outcome on the server's state」(见 :1357)。 ↩