在互联网的爆炸的时代下,”微服务”的概念也越来越被大家接受并应用于实践,日益增多的web service使RESTful越来越热。本文整理一下我对restful架构风格的一些理解。 🥕
REST
不知道有多少人一开始跟我一样,REST和RESTful傻傻分不清楚,一开始先扫个盲。
REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。Fielding非常有名的,想了解的话自行google一下。REST是Representational State Transfer的缩写。 阮一峰对这个词组的翻译是”表现层状态转化”。REST的名称”表现层状态转化”中,省略了主语。”表现层”其实指的是”资源”(Resources)的”表现层”。而资源通常对于我们来说可以是任何东西,而与之对应就是一个URI。
所以REST是一套原则,而符合REST原则的就是符合RESTful架构。
RESTful
HTTP动词
RESTful将对HTTP的动词按着它们的语义来定义
- 1 GET(SELECT):从服务器取出资源(一项或多项)。
- 2 POST(CREATE):在服务器新建一个资源。
- 3 PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
- 4 PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
- 5 DELETE(DELETE):从服务器删除资源。
- 6 HEAD:获取资源的元数据。(不常用)
- 7 OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。(跨域的时候会出现)
这里大家一般都知道,也都按着这套原则去规范接口,但是RESTful不单单如此。
有状态与无状态
所谓无状态的,即所有的资源,都可以通过URI定位,而且这个定位与其他资源无关,也不会因为其他资源的变化而改变。
绝大多数的时候,我们的系统都是有状态的,至少我遇到的大多数都是如此,我们虽然想用RESTful架构来设计,但是设计的确实貌合神离。那什么是有状态呢?
举个例子让我们更好的理解,比如我们有一个用户管理系统来说,如果我们要修改一个用户的信息,那我们通常需要先登录,因为我们要判断这个操作者是否有这个权限能进行操作。则这种情况是有状态的,因为我们的每一步操作都依赖于前一步操作,只要前置操作不成功,后续操作就无法执行。
如果我们通过一个特定URI可以直接对用户信息进行修改,这样的方式就是无状态的。无状态指的是任意一个web请求完全与其他请求隔离,当请求发出时,请求本身带上了这一请求所需的全部信息。这样的设计非常贴合分布式系统。当一个服务器宕机,对于无状态是毫无影响的,但是对于有状态,那么该用户最近的所有交互操作将无法被透明地移送至备用服务器上,除非该服务器时刻与主服务器同步全部用户的状态信息,这样的代价是很昂贵的。
有状态和无状态与请求本身没有多大关联,重要的是状态信息是由请求方还是响应方负责保存。
认证机制
从有状态变为无状态,我们将原先在服务端的状态抽离了出来,那我们的状态需要保存在哪,这就会变成一个问题。一般认证机制会有session auth(即通过用户名密码登录),basic auth,token auth和OAuth,服务开发中常用的认证机制为后三者,我们着重记录一下token auth和OAuth,其他用的比较少(至少我没怎么见到),有兴趣的可以自行google。
token auth
token大家都不陌生,token auth是在服务器按照一些规则(自己设计)生成token,服务端可以通过缓存或者其他方式存下token,并设置其过期时间。然后客户端登录将得到这个token,客户端保存,每次进行访问的时候,在header中带上token,我们在服务端可以简单的拦截请求,通过如下接收
String token =request.getHeader(“Authorization”);
OAuth2.0
OAuth(开放授权)是一个开放的授权标准,允许用户让第三方应用访问该用户在某一web服务上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
具体内容我们可以看下这篇文章 理解OAuth 2.0
设计思想
RESTful和以前的接口设计思想有很大的不同,我们以前设计接口,往往核心在于操作的角度来设计。REST的核心在于资源,资源是第一位的考虑,首先从资源的角度进行系统的拆分、设计。
我们沿用之前的demo,如果要修改一个用户的信息,那我们原先的设计思想将会是:
- 1 有修改用户信息功能
- 2 修改用户信息需要一个URL
- 3 定义好URL的参数
- 4 开始coding
这样我们能很快的把我们所要的接口写出来,我们是讲这个操作具体化出来,然后围绕这个操作进行设计,再将需要的东西补充进来。如果我们考虑的很充分,那肯定不会有什么问题,但是有时候考虑不周的时候,我们可能会遇到某个操作,需要再某个操作之后,就是操作有依赖,少的时候还好,但是操作多起来难免就增加了开发难度,以及接口命名难以统一。
当我们用资源的角度来设计接口,上面我们介绍了http的动词,按着规范我们可以很简单的统一命名,其次资源之间可能有关联,但是可以简单的进行切割,避免杂乱的依赖关系。
我们从http的动词可以看出,RESTful很容易对单一的资源进行设计。但是我们往往会有许多批量的操作,这就是一个设计的难点了。每个人都会有自己的设计方案,我们可以取长补短,学习一下别人的精华之处。下面举一些比较好的批量操作的设计例子,设计不需要太纠结于定义,毕竟是使用的工具,用的顺手才好。
GET /7le/instance/batch?id=aa,bb,cc:简约的设计
GET /7le/instance?batch={“ids”:[“aa”,”bb”,”cc”]}:健壮的设计
GET /7le/instance?batch=[{“method”:”DELETE”,”id”:”aa”},{“method”:”DELETE”,”id”:”bb”},
{“method”:”DELETE”,”id”:”cc”}]:facebook 的设计(墙外)