📘 第二章 简单的HTTP协议
📄 一句话概括

方法就是请求行第一个词。HTTP/1.1 定了八个,日常只用五个。语义靠约定,协议只认名字。

📝 本节和前面几节的关系

上一节看第二段(目标),这一节看第一段(方法)。 方法是「我想干什么」,也是缓存、重试、幂等判断的唯一依据。

一、八个方法各管什么

方法 语义 幂等 有正文
GET 取资源 是 否
HEAD 只取首部 是 否
POST 提交处理 否 是
PUT 整体替换 是 是
PATCH 局部更新 否 是
DELETE 删除 是 否
OPTIONS 问支持什么 是 否
TRACE 回显请求 是 否
「幂等」指执行一次和执行多次效果相同。
它不是「没有副作用」——DELETE 有副作用(东西没了),但删两次和删一次结果一样,所以幂等。
PUT 幂等而 POST 不幂等,是两者最本质的区别:PUT 的意思是「把资源变成这样」,重复执行结果不变;
POST 的意思是「请处理这件事」,重复执行可能下单两次。

二、协议不强制语义

Demo 里八种方法都发出去了,服务端全部照收——因为它只认方法名,不校验语义。 你发 DELETE 它也可以当 GET 处理。 所以安全边界必须在应用层实现:方法名不是权限,真正的校验是认证与授权(第八章)。 这一点在渗透测试里天天被利用。 TRACE 几乎是历史遗留:它会原样回显请求(包括 Cookie),是典型的 XST 攻击载体, 主流服务器与浏览器默认关闭。书里还列着它,但 2026 年的现实是基本见不到。

三、HEAD 和 OPTIONS 的实际用途

HEAD 拿首部不拿正文,用来检查「资源还在不在、多大、什么时候改的」,省掉整个正文的传输。 下载工具判断断点续传时就用它。 OPTIONS 拿 Allow 首部,知道这个地址支持哪些方法。 更重要的是 CORS 预检:浏览器跨域发非简单请求前,会先发一个 OPTIONS 问「你允不允许」, 服务端必须回一堆 Access-Control-* 首部。 顺带说一句:PATCH 是 2010 年才补进标准的(RFC 5789),比其它七个晚很多,所以老书里没有。

四、动手 Demo:八种方法真发一遍

逐个构造请求发出去,服务端把收到的请求行和正文长度记下来。 HEAD 与 OPTIONS 用专门的响应形态,其余回 200。

methods = [(u'GET', b'', u'取资源'),
           (u'POST', b'a=1', u'提交数据,非幂等'),
           (u'PUT', b'a=1', u'整体替换,幂等'),
           (u'DELETE', b'', u'删除,幂等'),
           (u'HEAD', b'', u'只要首部不要正文'),
           (u'OPTIONS', b'', u'问支持哪些方法'),
           (u'PATCH', b'a=1', u'局部更新,非幂等'),
           (u'TRACE', b'', u'回显请求,几乎全被禁用')]

实跑输出:

--------------------------------------------------------------------
八种方法真发一遍,看服务端收到什么
--------------------------------------------------------------------
  GET          GET /res HTTP/1.1(正文 0 字节)
      含义     取资源
  POST         POST /res HTTP/1.1(正文 3 字节)
      含义     提交数据,非幂等
  PUT          PUT /res HTTP/1.1(正文 3 字节)
      含义     整体替换,幂等
  DELETE       DELETE /res HTTP/1.1(正文 0 字节)
      含义     删除,幂等
  HEAD         HEAD /res HTTP/1.1(正文 0 字节)
      含义     只要首部不要正文
  OPTIONS      OPTIONS /res HTTP/1.1(正文 0 字节)
      含义     问支持哪些方法
  PATCH        PATCH /res HTTP/1.1(正文 3 字节)
      含义     局部更新,非幂等
  TRACE        TRACE /res HTTP/1.1(正文 0 字节)
      含义     回显请求,几乎全被禁用

  实发成功                   8 种
  HEAD 的响应有首部、没正文;OPTIONS 的响应含 Allow 首部——这两处最容易漏。
  服务端只认方法名,不认语义:你发 DELETE 它也可以当 GET 处理,
  语义约束在应用层,协议本身不强制。

实发成功 8 种,其中 POST/PUT/PATCH 三行的正文是 3 字节(a=1), 其余 0 字节——有正文的只有这三个(PATCH 也是)。 HEAD 的响应有首部没正文,OPTIONS 的响应带 Allow 首部。 这两处是最容易写错的地方:很多人用 Content-Length 判断响应是否完整, 遇到 HEAD 会一直等一个永远不来的正文。 服务端全部照收,说明方法名不构成任何安全边界。

30 秒实验

  1. 把 DELETE 换成 delete(小写),看服务端收到的是不是原样(是)。 想想真实服务器怎么处理——多数会 405 或 400。
  2. 给 GET 加上正文,看服务端收不收(收,但语义未定义,很多代理会丢掉)。
  3. 用 OPTIONS * 问整台服务器,看响应里 Allow 怎么写。

常见误区

  1. 「GET 和 POST 的唯一区别是有没有正文」——真正的区别是语义与幂等性, GET 也能带正文(虽然不推荐)。
  2. 「POST 比 GET 安全」——两者在网络上都是明文,HTTPS 才有加密;POST 只是不会留在浏览历史里。
  3. 「方法名能当权限用」——协议不管这个,必须在应用层校验。
自测三问(点开对答案) 1. 幂等的准确定义? —— 执行一次与执行多次,对资源状态的影响相同。不要求「无副作用」。 2. 为什么 PUT 幂等而 POST 不幂等? —— PUT 的语义是「把资源设为这个样子」,重复执行结果一样; POST 是「请处理」,每执行一次可能产生一个新结果。 3. HEAD 请求的响应有什么特别? —— 有首部和 Content-Length,但**没有正文**。按长度等待正文会一直卡住。

方法列完了。下一节换个角度:这些方法为什么这么设计。

相关笔记