极客时间已完结课程限时免费阅读

10 | 应该如何理解请求方法?

10 | 应该如何理解请求方法?-极客时间

10 | 应该如何理解请求方法?

讲述:Chrono

时长10:18大小11.77M

上一讲我介绍了 HTTP 的报文结构,它是由 header+body 构成,请求头里有请求方法和请求目标,响应头里有状态码和原因短语,今天要说的就是请求头里的请求方法。

标准请求方法

HTTP 协议里为什么要有“请求方法”这个东西呢?
这就要从 HTTP 协议设计时的定位说起了。还记得吗?蒂姆·伯纳斯 - 李最初设想的是要用 HTTP 协议构建一个超链接文档系统,使用 URI 来定位这些文档,也就是资源。那么,该怎么在协议里操作这些资源呢?
很显然,需要有某种“动作的指示”,告诉操作这些资源的方式。所以,就这么出现了“请求方法”。它的实际含义就是客户端发出了一个“动作指令”,要求服务器端对 URI 定位的资源执行这个动作。
目前 HTTP/1.1 规定了八种方法,单词都必须是大写的形式,我先简单地列把它们列出来,后面再详细讲解。
GET:获取资源,可以理解为读取或者下载数据;
HEAD:获取资源的元信息;
POST:向资源提交数据,相当于写入或上传数据;
PUT:类似 POST;
DELETE:删除资源;
CONNECT:建立特殊的连接隧道;
OPTIONS:列出可对资源实行的方法;
TRACE:追踪请求 - 响应的传输路径。
看看这些方法,是不是有点像对文件或数据库的“增删改查”操作,只不过这些动作操作的目标不是本地资源,而是远程服务器上的资源,所以只能由客户端“请求”或者“指示”服务器来完成。
既然请求方法是一个“指示”,那么客户端自然就没有决定权,服务器掌控着所有资源,也就有绝对的决策权力。它收到 HTTP 请求报文后,看到里面的请求方法,可以执行也可以拒绝,或者改变动作的含义,毕竟 HTTP 是一个“协议”,两边都要“商量着来”。
比如,你发起了一个 GET 请求,想获取“/orders”这个文件,但这个文件保密级别比较高,不是谁都能看的,服务器就可以有如下的几种响应方式:
假装这个文件不存在,直接返回一个 404 Not found 报文;
稍微友好一点,明确告诉你有这个文件,但不允许访问,返回一个 403 Forbidden;
再宽松一些,返回 405 Method Not Allowed,然后用 Allow 头告诉你可以用 HEAD 方法获取文件的元信息。

GET/HEAD

虽然 HTTP/1.1 里规定了八种请求方法,但只有前四个是比较常用的,所以我们先来看一下这四个方法。
GET 方法应该是 HTTP 协议里最知名的请求方法了,也应该是用的最多的,自 0.9 版出现并一直被保留至今,是名副其实的“元老”。
它的含义是请求从服务器获取资源,这个资源既可以是静态的文本、页面、图片、视频,也可以是由 PHP、Java 动态生成的页面或者其他格式的数据。
GET 方法虽然基本动作比较简单,但搭配 URI 和其他头字段就能实现对资源更精细的操作。
例如,在 URI 后使用“#”,就可以在获取页面后直接定位到某个标签所在的位置;使用 If-Modified-Since 字段就变成了“有条件的请求”,仅当资源被修改时才会执行获取动作;使用 Range 字段就是“范围请求”,只获取资源的一部分数据。
HEAD 方法与 GET 方法类似,也是请求从服务器获取资源,服务器的处理机制也是一样的,但服务器不会返回请求的实体数据,只会传回响应头,也就是资源的“元信息”。
HEAD 方法可以看做是 GET 方法的一个“简化版”或者“轻量版”。因为它的响应头与 GET 完全相同,所以可以用在很多并不真正需要资源的场合,避免传输 body 数据的浪费。
比如,想要检查一个文件是否存在,只要发个 HEAD 请求就可以了,没有必要用 GET 把整个文件都取下来。再比如,要检查文件是否有最新版本,同样也应该用 HEAD,服务器会在响应头里把文件的修改时间传回来。
你可以在实验环境里试一下这两个方法,运行 Telnet,分别向 URI“/10-1”发送 GET 和 HEAD 请求,观察一下响应头是否一致。
GET /10-1 HTTP/1.1
Host: www.chrono.com
HEAD /10-1 HTTP/1.1
Host: www.chrono.com

POST/PUT

接下来要说的是 POSTPUT 方法,这两个方法也很像。
GET 和 HEAD 方法是从服务器获取数据,而 POST 和 PUT 方法则是相反操作,向 URI 指定的资源提交数据,数据就放在报文的 body 里。
POST 也是一个经常用到的请求方法,使用频率应该是仅次于 GET,应用的场景也非常多,只要向服务器发送数据,用的大多数都是 POST。
比如,你上论坛灌水,敲了一堆字后点击“发帖”按钮,浏览器就执行了一次 POST 请求,把你的文字放进报文的 body 里,然后拼好 POST 请求头,通过 TCP 协议发给服务器。
又比如,你上购物网站,看到了一件心仪的商品,点击“加入购物车”,这时也会有 POST 请求,浏览器会把商品 ID 发给服务器,服务器再把 ID 写入你的购物车相关的数据库记录。
PUT 的作用与 POST 类似,也可以向服务器提交数据,但与 POST 存在微妙的不同,通常 POST 表示的是“新建”“create”的含义,而 PUT 则是“修改”“update”的含义。
在实际应用中,PUT 用到的比较少。而且,因为它与 POST 的语义、功能太过近似,有的服务器甚至就直接禁止使用 PUT 方法,只用 POST 方法上传数据。
实验环境的“/10-2”模拟了 POST 和 PUT 方法的处理过程,你仍然可以用 Telnet 发送测试请求,看看运行的效果。注意,在发送请求时,头字段“Content-Length”一定要写对,是空行后 body 的长度:
POST /10-2 HTTP/1.1
Host: www.chrono.com
Content-Length: 17
POST DATA IS HERE
PUT /10-2 HTTP/1.1
Host: www.chrono.com
Content-Length: 16
PUT DATA IS HERE

其他方法

讲完了 GET/HEAD/POST/PUT,还剩下四个标准请求方法,它们属于比较“冷僻”的方法,应用的不是很多。
DELETE 方法指示服务器删除资源,因为这个动作危险性太大,所以通常服务器不会执行真正的删除操作,而是对资源做一个删除标记。当然,更多的时候服务器就直接不处理 DELETE 请求。
CONNECT 是一个比较特殊的方法,要求服务器为客户端和另一台远程服务器建立一条特殊的连接隧道,这时 Web 服务器在中间充当了代理的角色。
OPTIONS 方法要求服务器列出可对资源实行的操作方法,在响应头的 Allow 字段里返回。它的功能很有限,用处也不大,有的服务器(例如 Nginx)干脆就没有实现对它的支持。
TRACE 方法多用于对 HTTP 链路的测试或诊断,可以显示出请求 - 响应的传输路径。它的本意是好的,但存在漏洞,会泄漏网站的信息,所以 Web 服务器通常也是禁止使用。

扩展方法

虽然 HTTP/1.1 里规定了八种请求方法,但它并没有限制我们只能用这八种方法,这也体现了 HTTP 协议良好的扩展性,我们可以任意添加请求动作,只要请求方和响应方都能理解就行。
例如著名的愚人节玩笑 RFC2324,它定义了协议 HTCPCP,即“超文本咖啡壶控制协议”,为 HTTP 协议增加了用来煮咖啡的 BREW 方法,要求添牛奶的 WHEN 方法。
此外,还有一些得到了实际应用的请求方法(WebDAV),例如 MKCOL、COPY、MOVE、LOCK、UNLOCK、PATCH 等。如果有合适的场景,你也可以把它们应用到自己的系统里,比如用 LOCK 方法锁定资源暂时不允许修改,或者使用 PATCH 方法给资源打个小补丁,部分更新数据。但因为这些方法是非标准的,所以需要为客户端和服务器编写额外的代码才能添加支持。
当然了,你也完全可以根据实际需求,自己发明新的方法,比如“PULL”拉取某些资源到本地,“PURGE”清理某个目录下的所有缓存数据。

安全与幂等

关于请求方法还有两个面试时有可能会问到、比较重要的概念:安全幂等
在 HTTP 协议里,所谓的“安全”是指请求方法不会“破坏”服务器上的资源,即不会对服务器上的资源造成实质的修改。
按照这个定义,只有 GET 和 HEAD 方法是“安全”的,因为它们是“只读”操作,只要服务器不故意曲解请求方法的处理方式,无论 GET 和 HEAD 操作多少次,服务器上的数据都是“安全的”。
而 POST/PUT/DELETE 操作会修改服务器上的资源,增加或删除数据,所以是“不安全”的。
所谓的“幂等”实际上是一个数学用语,被借用到了 HTTP 协议里,意思是多次执行相同的操作,结果也都是相同的,即多次“幂”后结果“相等”。
很显然,GET 和 HEAD 既是安全的也是幂等的,DELETE 可以多次删除同一个资源,效果都是“资源不存在”,所以也是幂等的。
POST 和 PUT 的幂等性质就略费解一点。
按照 RFC 里的语义,POST 是“新增或提交数据”,多次提交数据会创建多个资源,所以不是幂等的;而 PUT 是“替换或更新数据”,多次更新一个资源,资源还是会第一次更新的状态,所以是幂等的。
我对你的建议是,你可以对比一下 SQL 来加深理解:把 POST 理解成 INSERT,把 PUT 理解成 UPDATE,这样就很清楚了。多次 INSERT 会添加多条记录,而多次 UPDATE 只操作一条记录,而且效果相同。

小结

今天我们学习了 HTTP 报文里请求方法相关的知识,简单小结一下。
请求方法是客户端发出的、要求服务器执行的、对资源的一种操作;
请求方法是对服务器的“指示”,真正应如何处理由服务器来决定;
最常用的请求方法是 GET 和 POST,分别是获取数据和发送数据;
HEAD 方法是轻量级的 GET,用来获取资源的元信息;
PUT 基本上是 POST 的同义词,多用于更新数据;
“安全”与“幂等”是描述请求方法的两个重要属性,具有理论指导意义,可以帮助我们设计系统。

课下作业

你能把 GET/POST 等请求方法对应到数据库的“增删改查”操作吗?请求头应该如何设计呢?
你觉得 TRACE/OPTIONS/CONNECT 方法能够用 GET 或 POST 间接实现吗?
欢迎你把自己的答案写在留言区,与我和其他同学一起讨论。如果你觉得有所收获,欢迎你把文章分享给你的朋友。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 30

提建议

上一篇
09 | HTTP报文是什么样子的?
下一篇
11 | 你能写出正确的网址吗?
unpreview
 写留言

精选留言(66)

  • GitHubGanKai
    2020-01-07
    老师,这是我第三次刷这个专栏了,面试的时候,有个问题,想请教您一下,还希望你能解答一下:请求行 + 头部信息 + 空白行 + body ,有被问到说空白行的意义,我一直以为就是纯粹来标识 headers 的结束,但是面试官说不止这个功能,我后面看了HTTP 权威指南 也没有找到,Stack Overflow 也没找到。。。希望老师可以跟我说一下。

    作者回复: 按照http协议,空白行就是为了分隔header和body,因为http是纯文本的协议。 不知道面试官还能有什么新的解释,也许是故弄玄虚。 遇到这样的人可以及时请教,不能让他话只说一半。

    共 7 条评论
    78
  • 一步
    2019-06-19
    OPTIONS 方法还是用的很多的,CORS跨域请求必须用到OPTIONS方法了

    作者回复: 我接触的领域里options用的比较少,可能有点孤陋寡闻了。

    共 9 条评论
    39
  • nora
    2019-11-26
    pwq309同学的解释,可以说是很清晰了。 跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。 在现在前端最常用的 cors 跨域中,浏览器都是用 OPTIONS 方法发预检请求的
    展开

    作者回复: 赞!

    共 3 条评论
    34
  • 你好旅行者
    2019-06-19
    关于Post和Get之间的区别,我一直很困惑,因为其实我们也可以在Get的请求体里写参数,用Get去修改资源;或者在Post的请求头上去写参数,用它去获取资源。所以他们两者之间到底有什么区别呢?希望老师可以指教一下!

    作者回复: 就是个使用的习惯和约定,就像是红绿灯,不是强制要求你必须遵守,但大家都按照这样做沟通起来顺畅。 要理解协议的含义,要求你遵守,但不强制。

    共 4 条评论
    21
  • 壹笙☞漂泊
    2019-06-19
    答题: 1、增:POST 删:DELETE 改:PUT 查:GET 请求头如何设计,这个问题。。。不太明白。 2、我认为可以,因为http协议具有很好的灵活性。具体的对资源操作是由服务器决定的。 总结: Http/1.1规定了八种方法,单词必须都是大写的形式。 1. GET:获取资源,可以理解为读取或者下载数据 2. HEAD:获取资源的元信息; 3. POST:向资源提交数据,相当于写入或上传数据; 4. PUT:类似POST; 5. DELETE:删除资源; 6. CONNECT:建立特殊的连接隧道; 7. OPTIONS:列出可对资源实行的方法; 8. TRACE:追踪请求-响应的传输路径。 GET/HEAD ——从服务器获取资源 HEAD和GET类似,也是从服务器获取资源,但是不会返回请求的实体数据,只有响应头(元信息),是GET的简易版,如果不需要资源的话,可以避免传输body数据的浪费。 POST/PUT ——向服务器提交数据,数据在body里 PUT和POST作用类似,有微妙不同,通常POST标识新建,PUT标识修改 DELETE ——删除资源,危险性大,很少用 CONNECT ——要求服务器为客户端和另一台远程服务器建立一条特殊的链接,这时Web服务器充当代理的角色 OPTIONS ——要求服务器列出可对资源实行的操作方法,在响应头Allow字段里返回。功能有限,用处不大。Nginx没支持 TRACE ——用于对HTTP链路的测试或诊断,可以显示出请求 - 响应的传输路径。存在漏洞,会泄露网站的信息,所以通常也是禁止使用 安全与幂等 安全:在HTTP协议里,所谓的安全,是指请求方法不会对服务器上的资源造成实质的修改,so 只有GET和HEAD是安全的,因为是只读操作。 幂等:多次执行相同的操作,结果也都是相同的。so GET和HEAD 即是安全的也是幂等的,DELETE可以多次删除同一个资源,效果都是“资源不存在”,所以也是幂等。POST是新增或提交数据,多次提交会创建多个资源,所以不是幂等的。PUT是替换或更新数据,多次更新一个资源,资源还是第一次更新的状态。所以是幂等的。 幂等:GET、HEAD、DELETE、PUT 非幂等:POST
    展开

    作者回复: 总结的非常好。 问题里的“请求头如何设计”,意思是说相关的curd参数应该放在什么地方,比如用query参数或者是字段,只是一个提示,不是要必须如何如何做。

    共 3 条评论
    11
  • 潇潇雨歇
    2020-06-12
    1、 GET:查 POST:增 PUT:改 DELETE:删 请求行按 请求方法 请求资源 http版本号 GET参数直接放在URI里,其他的放body 2、应该可以
    展开

    作者回复: 对。

    6
  • 彧豪
    2019-07-23
    老师,关于options请求,我有一些疑问,就是这个请求似乎是不受前端开发和服务端开发控制的是不是?get,post或者delete之前都有可能发出,那它的出现时机或机理是怎样的呢,为何会发出options请求?我发现我平时用webpack的代理来开发,代理到测试/生产环境的时候,get/post/delete请求之前没有options请求,但是部署到测试环境或者生产环境之后就会在get/post/delete请求之前会发出一个options请求,然后我司的其他前端和java工程师都说不上个所以然来,我也不懂,所以想要问下老师看看,希望老师能指点迷津
    展开

    作者回复: options请求会在allow字段里列出可以对资源实施的操作方法,比如有的资源只支持get,有的只支持post/delete。 所以有的时候回先发一个options,来确定应该如何操作资源。 不过这个方法用的很少,其实没太大用处,因为即使资源不支持某种方法,直接返回一个405或者其他的状态码就可以了。

    共 3 条评论
    6
  • 浩浩
    2019-06-19
    老师有个问题想问一下,我之前用比较旧的http工具类发请求,使用get方法时不能使用请求体,但使用软件工具能使用请求体,不同方法对请求的格式要求是不是一样,还有就是不同方法的区别是不是体现在服务器的响应上,用post请求访问一张照片会是什么样的,暂时还没试过

    作者回复: 任何请求报文都可以带请求体,与方法无关,老的工具可能对协议支持的不好。 后面你理解的对,请求方法最终如何处理还是要依赖于服务器,如果愿意,服务器也可以用get来上传数据,用post获取数据,现在的get/post都是依据协议标准来执行,所以是get获取post上传。

    4
  • 大小兵
    2019-06-19
    真希望快点更新啊,看的不过瘾!

    作者回复: 慢慢来。

    3
  • 业余爱好者
    2019-06-19
    之前做一个网站的cms,觉得又是一套crud,毫无新意,闲得慌,于是玩了一波restful"架构"。严格按照http规范,比如,查询都用GET,新增用POST,更新用PUT,删除用DELETE,url的设计也按照rest风格设计。现在想想,tomcat支持这几种http方法也是万幸,不然的话,又得加班换成get/post了。 这段经历我认识到,http只是一种协议,不同的服务器,还有客户端,比如浏览器都可以有自己的实现。虽然各自在实现上有所取舍,但大体上,按照协议规范来,不会差。 协议,是个好东西。。
    展开

    作者回复: 请求方法的设计思想非常好,动词可以表示各种操作,所以非常适合RESTful。

    共 2 条评论
    3
  • 1900
    2019-06-19
    “幂等”有什么具体的落地场景么?它重要的原因在哪里呢?

    作者回复: 在RESTful设计的时候,要考虑动作对服务器内部状态的影响。 这个比较理论,一般不太需要关心。

    共 4 条评论
    2
  • Geek_Maggie
    2021-03-11
    你能把 GET/POST 等请求方法对应到数据库的“增删改查”操作吗? GET:查 POST:增 DELETE:删 PUT:改 请求头应该如何设计呢? 按照:请求方法+URI+HTTP版本号 你觉得 TRACE/OPTIONS/CONNECT 方法能够用 GET 或 POST 间接实现吗? 可以 TRACE:显示出请求 - 响应的传输路径,能用GET请求间接实现;
    展开

    作者回复: 回答的很好,不过要注意,http版本号应该算是历史遗留问题了,没什么太大用处,只是为了兼容而保留。

    1
  • 勇敢黄瓜
    2020-04-23
    1. 你能把 GET/POST 等请求方法对应到数据库的“增删改查”操作吗?请求头应该如何设计呢? GET请求对应select操作;POST请求对应insert操作;GET请求,可以将用户密码放在header,将查询参数,放在请求参数中;POST请求,可以将用户密码放在header,将数据放在body,将新增数据的位置放在url以及请求参数; 2. 你觉得 TRACE/OPTIONS/CONNECT 方法能够用 GET 或 POST 间接实现吗? TRACE用于跟踪请求,可以在GET或POST请求的响应头加上诊断相关的参数,比如请求耗时等; OPTIONS用于获取请求方法,可以用GET请求访问'/options'之类的uri,服务端响应中返回允许方法; CONNECT用于创建隧道,需要用POST请求触发隧道创建;
    展开

    作者回复: 回答的挺好。 对于第一个,注意HTTP是明文协议,直接传输密码不够安全。

    1
  • Geek_steven_wang
    2019-08-13
    既然不同服务器支持的方法不同,是不是最好把不支持的方法在服务器侧明确返回404 或503。这样更安全。 关于幂等,其实是协议规定了get delete put 为幂等,服务器开发时要尽量实现为幂等,其实是对服务器实现的一个要求,当然开发人员可以不实现。

    作者回复: 是的,你理解的很对。 协议就是一个要求通信双方都遵守的约定,当然也可以不遵守。

    1
  • 温木
    2019-08-06
    学到新的知识点,幂等,很不错,老师辛苦了

    作者回复: 不客气。

    1
  • 陈1016
    2019-06-28
    老师您好,关于 “DELETE 可以多次删除同一个资源,效果都是‘资源不存在’,所以也是幂等” 这句话,我个人理解是:假设服务器接受这个请求,允许对应操作。如果初始时有资源,第一次,就是“操作成功”,第二次开始,后续都是“资源不存在”;如果一开始没有这个资源,那么,一直都是“资源不存在”。那么,DELETE,是否可以算作“可变幂等”?或者,换个说法:删除操作成功后,从下一次开始的后续DELETE操作,都是“幂等”

    作者回复: “幂等”说的是多次操作效果相同,所以不管资源是否存在,操作多次以后结果都是一样的,所以delete是“幂等”的。 你说的“可变幂等”没有这个说法,所以不成立。

    1
  • 一步
    2019-06-19
    老师 WebDav 这一块会详细讲嘛? 有这块的需求,或者有没有好的文档

    作者回复: 这块我基本没用过,它也不是http标准里面的,抱歉了。

    1
  • 10
    2019-06-19
    我采用POST /10-2 HTTP/1.1的指令写了10-2的内容为“POST DATA IS HERE”,然后我采用GET /10-2 HTTP/1.1的指令去读内容 返回的“200 OK”,但实体body的内容只是一个“0”,而非前面写的“POST DATA IS HERE” 请问难道我前面的POST指令没有写成功么?

    作者回复: 测试用的URI“10-2”不支持存储数据,所以post的数据只能在当次请求生效。 另外发现这两测试uri有小bug,已经修复,请及时git pull更新。

    1
  • 许童童
    2019-06-19
    请求头里面应该要包含 请求的目标 也就是对应数据库里面行
    1
  • 彧豪
    2019-06-19
    老师,话说我昨天实际项目终于到一个问题: get请求带上查询字符串例如?name=a+b,但是打开chrome的控制台network选项卡发现请求url那是对的,是xxx?name=a+b,但是最后的查询字符串那那里是name: a b,+号变成了空格,java那边收到的也是a b…… 最后的解决方法是我这边encodeURIComponent一下,java那边URLDecoder.decode一下即可 但是我不明白为何会如此,为何get请求的查询字符串中带+号,浏览器会将其变为空格 在浏览器控制台network选项卡底端的query string parameters那有个"view URL encoded"按钮,点了之后name:a b变为name:a+b,这就意味着空格被转义成了+号,那是不是这个a和b之间的字符(串)实际不是空格呢?毕竟我暂时想不到什么方法能将空格转义为+号 关于+号变空格的问题希望老师能指点迷津,感谢
    展开

    作者回复: 下一讲里面的url编码就会谈到。 uri里面有些字符是不允许出现的,需要编码和解码,而+正好就被解码成了空格。 你刚才也说了,会用到encodeURI这样的函数。

    1