细说RESTful API之幂等性

目录
接口幂等性的含义
幂等性原本是数学中的含义,表达式的是N次变换与1次变换的结果相同。
而RESTFul API中的幂等性是指调用某个方法1次或N次对资源产生的影响结果都是相同的,需要特别注意的是:这里幂等性指的是对资源产生的影响结果,而不是调用HTTP方法的返回结果。
举个例子,RESTFul API中的GET方法是查询资源信息,不会对资源产生影响,所以它是符合幂等性的,但是每次调用GET方法返回的结果有可能不同(可能资源的某个属性在调用GET方法之前已经被其他方法修改了)。
实际上,在分布式架构中的API幂等性不仅仅针对RESTFul接口,而是对所有类型的接口适用,目的是为了确保调用1次或N次接口时对资源的影响结果都是相同的。
接口符合幂等性有什么用处
接口的幂等性确保了无论调用1次还是N次对资源的影响都是相同的,这在某些场合下是非常有用的。
举个例子,有这样一个接口方法:pay(long account, int money),该方法用于银行卡扣款支付,参数account为账户ID,money为需要扣除的钱数。当用户从网页上点击支付按钮时,在该方法的实现逻辑中需要从指定账户中扣除对应的商品价钱。如果支付操作已经成功执行,但是响应消息因为某种原因未能及时返回给客户端,这时候给用户的体验是可能是未支付成功,如果此时再次点击支付按钮,那么将再一次执行该方法,结果可能会导致用户只买了一件商品却花了双份的钱,这当然是不合理的。整个流程如下图所示:

当然,就上述例子的场景,为了避免用户重复支付,是可以通过别的方式解决的,比如:分布式事务,或者根据支付状态提示给予用户进行提示等等。
但是,如果引入了分布式事务,那么将带来实现上的复杂性,而且会影响到接口性能;而采取提示信息的方式并不能百分之百确保用户不会重复支付,存在一定的风险。而如果接口符合幂等性,即:对同一个订单无论是执行一次支付还是多次支付,在服务端都确保只会扣一次款,那么既不需要引入分布式事务的复杂性,也能从根本上解决重复支付的问题,这也就是接口符合幂等性的价值所在。
总而言之,接口符合幂等性在可以降低系统实现的复杂性,并能保证资源状态的一致性。
HTTP方法的幂等性与安全性
RESTFul风格的接口设计本质上使用的是HTTP协议方法,因此,RESTFul接口方法的幂等性指的就是HTTP方法的幂等性。
常用的HTTP方法有:OPTIONS(获取服务器信息),HEAD(请求资源首部信息),GET(获取资源),POST(创建资源),PUT(更新资源全部信息),PATCH(更新资源部分信息),DELETE(删除资源)。那么,这些HTTP方法的幂等性又是什么样的呢?
除了幂等性之外,HTTP方法的安全性是指不对资源产生修改。
如下是常用HTTP方法的幂等性和安全性总结:
| HTTP方法名称 | 是否幂等 | 是否安全 |
|---|---|---|
| OPTIONS | Y | Y |
| HEAD | Y | Y |
| GET | Y | Y |
| PUT | Y | N |
| DELETE | Y | N |
| POST | N | N |
| PATCH | N | N |
从上述表格中可以看出,HTTP方法的幂等性和安全性并不是同一个概念,如下是对个各个方法的幂等性和安全性解释:
- OPTIONS方法常常用于获取服务器信息,不会对资源产生影响,也不会对资源进行修改,因此它是幂等的也是安全的;OPTIONS方法最常见的场景是在浏览器的跨域请求中,如果浏览器发起的是一个跨域访问的API(不论是GET方法还是POST方法),再真正发送业务的GET或POST方法之前会发送一个OPTIONS方法从服务端获取信息,从服务器返回的信息中得知该请求是否支持跨域访问,从而决定下一步是否能成功发送真正的业务请求。
- HEAD方法用于请求资源的头部信息,不会资源产生影响,也不会对资源进行修改,因此它是幂等的也是安全的。
- GET方法用于获取资源信息,虽然可能每次返回的结果都不相同,但是GET方法本身不会对资源产生影响,在RESTFul语义里GET方法也不会修改资源,因此它是幂等的,也是安全的。
- PUT方法在RESTFul语义里表示对资源进行全量更新,因此调用1次或N次的结果都是一致的,所以它是幂等的,但不是安全的。
- DELETE方法用于删除资源,调用1次或N次的结果都是相同的,因此是幂等的,但不是安全的。
- POST方法在RESTFul语义里表示新建资源,显然调用1次与调用N次的结果不同(调用1次新建1个资源,调用N次新建N个资源),因此不是幂等的,同时也不是安全的。
- PATCH方法在RESTFul语义里表示对资源的局部更新,因此不能保证调用1次与调用N次的结果相同(如:被更新的资源某个属性随着不同的调用次数在变化),所以不是幂等的,同时也不是安全的。
如何设计符合幂等性的接口
设计幂等性接口的关键在于保证接口不论是被调用1次还是N次,它对资源所产生的影响都是相同的。
从上述HTTP方法的幂等性总结中可以得知,HTTP协议的POST和PATCH方法都不是幂等性的(但是我们却经常会在RESTFul接口中使用到它们),那是否就意味中无法将POST和PATCH方法设计为幂等性接口了呢?答案显然是否定的。在上述例子中,可以将订单ID也作为方法参数之一,如:pay(long account, int money, long order),这样在服务端确保一个订单只会被支付一次(订单号是全局唯一的),那么无论该方法被调用1次还是N次结果都是一样的,也就保证了接口的幂等性。当然,在哪些没有订单号的场景,可以为接口操作生成一个全局唯一的处理号ID,并把该处理号ID作为方法参数之一,这样在服务端确保一个处理号ID只会被执行一次就保证了接口的幂等性。
符合幂等性的接口调用流程描述如下图所示:

写在最后
虽然说设计符合幂等性的接口在某些场合可以降低系统的复杂性(如:可以不用引入分布式事务),但是并非在所有场合的问题都能通过幂等性接口解决,在必要的时候依然需要引入分布式事务处理这样的框架。我们不要也不能把接口幂等性作为万能的解决办法,但是,我们在设计接口时尽量考虑符合幂等性处理是非常有价值的。
【参考】
http://blog.720ui.com/2016/restful_idempotent/ 如何理解RESTful的幂等性
https://www.cnblogs.com/weidagang2046/archive/2011/06/04/idempotence.html 理解HTTP幂等性
https://sofish.github.io/restcookbook/http%20methods/idempotency/ RESTful 手册
细说RESTful API之幂等性的更多相关文章
- 细说RESTFul API之版本管理
目录 接口实现版本管理的意义 如何实现接口的版本管理 项目实战 接口实现版本管理的意义 API版本管理的重要性不言而喻,对于API的设计者和使用者而言,版本管理都有着非常重要的意义. 首先,对于API ...
- 细说RESTful API之文档管理
目录 API文档格式 文档管理方式 基于注解实现,代码和文档在一起 Swagger Api2Doc 基于API测试工具生成 Postman rest-client 独立编写文档 RAP DOCleve ...
- 通过一组RESTful API暴露CQRS系统功能
命令和查询责任分离(CQRS)是由Greg Young提出的一种将系统的读(查询).写(命令)操作分离为两种独立子系统的架构模式.命令通常是异步执行的,并存储在一个事务型数据库中,而读操作则通常是最终 ...
- 利用 Django REST framework 编写 RESTful API
利用 Django REST framework 编写 RESTful API Updateat 2015/12/3: 增加 filter 最近在玩 Django,不得不说 rest_framewor ...
- 拿nodejs快速搭建简单Oauth认证和restful API server攻略
拿nodejs快速搭建简单Oauth认证和restful API server攻略:http://blog.csdn.net/zhaoweitco/article/details/21708955 最 ...
- HTTP methods 与 RESTful API
Note GET, primarily used to select resources. Other options for an API method include: POST, primari ...
- Restful API的设计规范
原文:https://blog.csdn.net/u013063153/article/details/72828164 本文总结了 RESTful API 设计相关的一些原则,只覆盖了常见的场景.有 ...
- restful API 规范(转)
1. URI URI 表示资源,资源一般对应服务器端领域模型中的实体类. URI规范 不用大写: 用中杠-不用下杠_: 参数列表要encode: URI中的名词表示资源集合,使用复数形式. 资源集合 ...
- 我所理解的Restful API最佳实践
一直在公司负责API数据接口的开发,期间也遇到了不小的坑,本篇博客算是做一个小小的记录. 1. 不要纠结于无意义的规范 在开始本文之前,我想先说这么一句:RESTful 真的很好,但它只是一种软 ...
随机推荐
- EF_DbHelper
最近在学校使用EF用到项目中,希望能够提高开发效率,所以从网上找点相关的代码,找到代码原型地址: https://github.com/kungge/EF_DbHelper 自己根据需要进行改造: 版 ...
- 项目Alpha冲刺(团队) -- 总结及汇总
项目Alpha冲刺(团队) --总结 1.团队信息 团队名 :男上加男 成员信息 : 队员学号 队员姓名 个人博客地址 备注 221600427 Alicesft https://www.cnblog ...
- Spring中AOP方式实现多数据源切换
作者:suroot spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性.而这样的方案就会不同于常见的单一数据实例的方案,这就要 ...
- Laravel 解决blade模板转义html标签问题
当我们使用富文本编译器(如:Ueditor编译器)保存编辑的内容后,在blade模板中,想要显示原生的html标签内容时该怎么做? 首先,了解下laravel {{ 变量名 }} 与{!! 变量名 ! ...
- 洛谷 P1993 小K的农场 题解
每日一题 day55 打卡 Analysis 这是我们一次考试的T1,但我忘了差分约束系统怎么写了,所以就直接输出Yes混了60分 首先转化题目: 1:表示农场 a 比农场 b 至少多种植了 c 个单 ...
- 【JZOJ6230】【20190625】梦批糼
题目 一个$n\times m \times l $的立方体,有一些位置有障碍 一次操作会随机选择一个立方体,共有\(w\)次操作 询问所有操作都不选到障碍点,被选到至少一次的点的期望 $n ,m,l ...
- 【luoguP2994】[USACO10OCT]吃晚饭的时候Dinner Time
题目链接 按顺序对于每个座位找一个最近的同时编号最小的牛就行了 #include<iostream> #include<cstring> #include<cstdio& ...
- shell 只读变量
test.sh: #!/bin/bash myUrl="http://www.google.com" readonly myUrl myUrl="http://www.r ...
- 将图片文件转成BASE64格式
html5Reader (file, item) { const reader = new FileReader() reader.onload = (e) => { this.$set(ite ...
- 分析WordPress数据表之用户表(功能篇)
数据表分析 wp_users wp_usermeta 用户系统就是靠着这两张表来实现的. 具体事例分析 添加用户 添加成功后,我们会分别在wp_users及wp_usermeta表中分别看到test0 ...