简介
一个HTTP方法是幂等的,指的是同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。换句话说就是,幂等方法不应该具有副作用(统计用途除外)。在正确实现的条件下,GET
,HEAD
,PUT
和DELETE
等方法都是幂等的,而 POST
方法不是。所有的 safe 方法(指不修改资源的 HTTP 方法)也都是幂等的。
幂等性只与后端服务器的实际状态有关,而每一次请求接收到的状态码不一定相同。例如,第一次调用DELETE
方法有可能返回 200
,但是后续的请求可能会返回404
。DELETE
的言外之意是,开发者不应该使用DELETE
方法实现具有删除最后条目功能的 RESTful API。
需要注意的是,服务器不一定会确保请求方法的幂等性,有些应用可能会错误地打破幂等性约束。
GET /pageX HTTP/1.1
是幂等的。连续调用多次,客户端接收到的结果都是一样的:
1 | GET /pageX HTTP/1.1 |
POST /add_row HTTP/1.1
不是幂等的。如果调用多次,就会增加多行记录:
1 | POST /add_row HTTP/1.1 |
DELETE /idX/delete HTTP/1.1
是幂等的,即便是不同请求之间接收到的状态码不一样:
1 | DELETE /idX/delete HTTP/1.1 -> Returns 200 if idX exists |
安全方法
安全方法是指不修改资源的 HTTP 方法。譬如,当使用 GET 或者 HEAD 作为资源 URL,都必须不去改变资源。然而,这并不全准确。意思是:它不改变资源的 表示形式。对于安全方法,它仍然可能改变服务器上的内容或资源,但这必须不导致不同的表现形式。
这表示下述是不对的,因为它实际上将删除博客文章:
1 | GET /blog/1234/delete HTTP/1.1 |
安全方法是那些可以被缓存、对资源无损预加载的方法。
幂等性分析
HTTP幂等方法,是指无论调用多少次都不会有不同结果的 HTTP 方法。不管你调用一次,还是调用一百次,一千次,结果都是相同的。
还是以之前的博文的例子为例。
1 | GET /tickets # 获取ticket列表 |
HTTP GET方法
HTTP GET方法,用于获取资源,不管调用多少次接口,结果都不会改变,所以是幂等的。
1 | GET /tickets # 获取ticket列表 |
只是查询数据,不会影响到资源的变化,因此我们认为它幂等。
值得注意,幂等性指的是作用于结果而非资源本身。怎么理解呢?例如,这个HTTP GET方法可能会每次得到不同的返回内容,但并不影响资源。
可能你会问有这种情况么?当然有咯。例如,我们有一个接口获取当前时间,我们就应该设计成
1 | GET /service_time # 获取服务器当前时间 |
它本身不会对资源本身产生影响,因此满足幂等性。
HTTP POST方法
HTTP POST方法是一个非幂等方法,因为调用多次,都将产生新的资源。
1 | POST /tickets # 新建一个ticket |
因为它会对资源本身产生影响,每次调用都会有新的资源产生,因此不满足幂等性。
HTTP PUT方法
HTTP PUT方法是不是幂等的呢?我们来看下
1 | PUT /tickets/12 # 更新ticket 12 |
因为它直接把实体部分的数据替换到服务器的资源,我们多次调用它,只会产生一次影响,但是有相同结果的 HTTP 方法,所以满足幂等性。
HTTP PATCH方法
HTTP PATCH方法是非幂等的。HTTP POST方法和HTTP PUT方法可能比较好理解,但是HTTP PATCH方法只是更新部分资源,怎么是非幂等的呢?
因为,PATCH提供的实体则需要根据程序或其它协议的定义,解析后在服务器上执行,以此来修改服务器上的资源。换句话说,PATCH请求是会执行某个程序的,如果重复提交,程序可能执行多次,对服务器上的资源就可能造成额外的影响,这就可以解释它为什么是非幂等的了。
可能你还不能理解这点。我们举个例子
1 | PATCH /tickets/12 # 更新ticket 12 |
此时,我们服务端对方法的处理是,当调用一次方法,更新部分字段,将这条ticket记录的操作记录加一,这次,每次调用的资源是不是变了呢,所以它是有可能是非幂等的操作。
HTTP DELETE方法
HTTP DELETE方法用于删除资源,会将资源删除。
1 | DELETE /tickets/12 # 删除ticekt 12 |
调用一次和多次对资源产生影响是相同的,所以也满足幂等性。
对比
(部分) HTTP 方法概览
HTTP Method | Idempotent | Safe |
---|---|---|
OPTIONS | yes | yes |
GET | yes | yes |
HEAD | yes | yes |
PUT | yes | no |
POST | no | no |
DELETE | yes | no |
PATCH | no | no |
如何设计符合幂等性的高质量RESTful API
HTTP GET方法 vs HTTP POST方法
也许,你会想起一个面试题。HTTP请求的GET与POST方式有什么区别?你可能会回答到:GET方式通过URL提交数据,数据在URL中可以看到;POST方式,数据放置在HTML HEADER内提交。但是,我们现在从RESTful的资源角度来看待问题,HTTP GET方法是幂等的,所以它适合作为查询操作,HTTP POST方法是非幂等的,所以用来表示新增操作。
但是,也有例外,我们有的时候可能需要把查询方法改造成HTTP POST方法。比如,超长(1k)的GET URL使用POST方法来替代,因为GET受到URL长度的限制。虽然,它不符合幂等性,但是它是一种折中的方案。
HTTP POST方法 vs HTTP PUT方法
对于HTTP POST方法和TTP PUT方法,我们一般的理解是POST表示创建资源,PUT表示更新资源。当然,这个是正确的理解。
但是,实际上,两个方法都用于创建资源,更为本质的差别是在幂等性。HTTP POST方法是非幂等,所以用来表示创建资源,HTTP PUT方法是幂等的,因此表示更新资源更加贴切。
HTTP PUT方法 vs HTTP PATCH方法
此时,你看会有另外一个问题。HTTP PUT方法和HTTP PATCH方法,都是用来表述更新资源,它们之间有什么区别呢?我们一般的理解是PUT表示更新全部资源,PATCH表示更新部分资源。首先,这个是我们遵守的第一准则。根据上面的描述,PATCH方法是非幂等的,因此我们在设计我们服务端的RESTful API的时候,也需要考虑。如果,我们想要明确的告诉调用者我们的资源是幂等的,我的设计更倾向于使用HTTP PUT方法。
本文整理自
[RESTful 手册](https://sofish.github.io/restcookbook/http methods/idempotency/)
仅做个人学习总结所用,遵循CC 4.0 BY-SA版权协议,如有侵权请联系删除!