REST的含义
REST
是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格,它不仅可应用于Web服务设计,但目前来说其最流行的领域便是Web
。RESTful
则是基于HTTP
语义化的设计风格,它并不是一种“标准”,只要设计者遵循RESTful
的设计规范,开发过程和应用便能享受到其带来的好处,但接口设计往往不是一尘不变的,有时候违反RESTful
规范的设计可能会带来更多便利(但这并不意味着不去遵守,而是应该将设计实现得更灵活)。下面提出REST得解释:
单词拆分 | 含义 |
---|---|
Resources | 资源(互联网得一切视作资源) |
Representational | 标志性(资源的表现层) |
State Transfer | 状态转移(服务器数据的改变) |
以上是REST本身得释义,我们将再下文详细讲述根据其含义如何设计标准的RESTful接口。
1.以资源为中心
我们将网络上的一切视资源,无论是图片、视频、文本还是链接、商品,都将他们当作资源。也正是基于这点,RESTful设计一切以资源为中心,设计原则对应着接口为名词表述形式,接口不能出现动词。
http://localhost/users
一般接口资源对象也对应着数据库中的某张数据表,例如上述接口对应数据库存在一张user表,开发围绕其进行增删改查操作,由于数据表多为数据集合,因此常常用复数形式体现。
相比于传统接口设计,RESTful风格接口围绕资源本身展开,资源和操作分离,更便于开发时资源的管理。
2.资源抽象
接口资源与数据库资源一一对应的情况是常见的,但这并不意味着所有资源都可以映射为数据库表资源。比如前端向后台请求视频格式资源,但开发中我们不会将庞大的视频资源以二进制文件形式(LONG_BLOB
)存储于数据库,如果这样做将导致IO负担严重加大,数据库读写性能变差;常规做法是将视频资源存放于图片视频资源服务器,用地址去请求资源,因此数据库应不该存在对应着的视频资源数据表,而是视频链接表,由后端自己请求获取后传前台。对于没有资源对应表情况下的名词设计,我们采用抽象方法来完成名词塑造,例如请求视频资源的格式可以是这样的:
http://localhost/videos
3.资源多样的表现层
传统的接口对于表现层的请求常常会在地址栏上体现,例如:
http://localhost/userinfo.json?userid=20191129
如此,该接口希望后端返回数据的格式以JSON
字符串形式,这样的写法是前后的一致讨论的结果,一定程度上复杂化了接口的表达,为此RESTful提供了新的表现层约束方式,是Http协议的规范,更利于敏捷开发(Agile software development),也正是REST含义中Representational
单词的释义。
对于Representational
的释义体现在其传输内容的表现,资源在网络传输中可以有各种形式的表现,可以是图片、视频也可以是普通文本、JSON
字符串,在web应用中我们可以设置请求头(Request Headers
)和响应头(Response Headers
)来约束内容形式,打开浏览器查看Network模块,当有请求发送时,我们可以观察请求头和响应头与响应头中的参数,在请求头中包含Accept
参数(该参数仅在请求头中存在),意为返回内容的MIME
格式而
ContentType
在请求头和响应头中都可以存在,表示数据在网络中传输的MIME
格式就此两者约束着内容在传输过程中的表现形式,也即
Representational
。
MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准: W3C手册
4.数据状态转移
对于Web开发来说,对Http协议可谓时重度依赖,但Http众所周知是无状态协议,几次请求之间无法建立关联,因此数据的状态全部都存在于后端服务器,主要表现在数据库的信息存储,因此接口的作用除了获取数据外,还可以改变后端数据的状态,因此接口调用产生的结果可以使数据发生状态转移。
5.资源接口设计
接口的设计是基于以上几点原则的,剩下的便是请求方式和返回值的处理
- 请求方式与操作
请求方式 | 操作 |
---|---|
GET | 获取资源 |
POST | 创建资源 |
PUT | 修改资源整体 |
PATCH | 修改资源元参数 |
DELETE | 删除资源 |
HEAD | |
OPTIONS |
以上就是Http1.1
协议提供的 7 种请求方式,被划线的操作并非不可用,而是不常用,一般我们基于前面 5 种请求方式做操作指令,例如我们要查询3号用户的信息
GET http://localhost/users/3
修改3号员工的一些信息
PUT http://localhost/users/3?age=25&phoneNum=135xxxxxxxx
删除3号员工信息
DELETE http://localhost/users/3
注意:当传输内容很多的情况下,我们可以通过表单传参的方式向后端传参数,例如创建资源(比如注册)一般我们使用表单传参,这样不会使URI
过度复杂且不会暴露参数。
- 操作与返回参数及状态码
操作 | 返回内容(状态码) |
---|---|
增加 | 新建资源完整对象(201) |
删除 | 空内容(No Content)(204) |
修改 | 修改资源完整对象(201) |
查询 | 查询资源(200) |
以上是RESTful规范规定的返回值与状态码,这也是基于Http协议2xx的返回码规范,但这并不意味着所有的接口都要设计成标准的RESTful风格接口,有时候返回值和返回状态码的设计可以参考传统接口设计,成功即200
。遵守规范的好处自不用多说,增强了接口的自述性。
6.抽象接口方法
基于以上几点我们已经可以设计出基础的RESTful风格API,但实践过程中毕然会发现,RESTful接口在增、删、改、查的功能体现出了天然的优势,不仅自述性强,操作、资源、表现形式解耦合,还利于缓存方便管理,但存在这样的问题,一个完整的Web
系统,绝不可能只存在最简单的CRUD
操作,除了前端可释义的基本操作(比如注册对应创建资源)外,还存在这多步资源操作,比如登录操作。登录基于查询功能,但它并不是简单的查询,而是查询带逻辑的业务,简单的查询接口向后台请求并不能得到用户登录状态的信息,因此对于此类接口,我们应额外抽象合理的方法请求,以登录为例:
GET http://localhost/session //登录信息以ajax或表单传
我们将登录过后的过程抽象为一次用户会话,登录成功即标志会话的开始,那么注销该怎么表示?
DELETE http://localhost/session/3
上述接口表示将3号用户注销,也就是删除3号用户的本次会话。
7.接口版本和完整表述
一个接口要不要加版本号?这个问题其实很简单,但也是一直在讨论的热门问题。我的回答是:根据软件的后期维护情况,考虑是否添加版本号。有两种情况——第一,我们作为乙方,只存在一次开发的义务,而甲方并没有后期版本迭代和维护的意向,此时我们可以考虑不添加版本信息;第二,一个正常的系统势必要经过长期的版本迭代,因此版本号是必要的。
如果我需要做版本迭代,但就是不想加版本号?
这个情况会造成C/S客户端运行异常,新版本用户正常使用软件,而老版本用户会因为没有更新客户端而导致功能出现异常甚至崩溃。
另外,一个完善的接口应该分功能模块,简单的接口不能保证接口冲突。例如,我们加上语义化版本和模块信息后的完整接口可以是这样的:
GET http://localhost/ver1.0.0/lightingshop/users
上述接口表示
- 基于RESTful风格设计查询接口
- 产品版本为1.0.0
- 作用是向后端请求lightingshop模块的user表种所有用户信息
总结
RESTful设计已经成为Web应用设计的热门,无论是类似于前后端接口文档说明的软件Swagger还是风靡微服务领域的一站式解决方案SpringCloud,都采用的RESTful API的形式。无疑RESTful是一种十分简洁方便且益处良多的设计风格,顾名思义,放松地做接口设计,轻松的做开发。Why not have a try?