关注微信公众号“池边小树”~~获取更多分析~~
1 Rest和Restful架构
Rest的全称为Representational State Transfer,中文翻译“表述性状态转移”或“表现层状态变化”。如果一个架构符合REST原则,则称它为Restful架构。
1.1 相关概念的理解[1]
(1)资源(Resources)
REST的名称“Representational State Transfer”中,省略了主语。其实指的是“资源(Resources)”的“表述性状态转移”。
所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。
(2)表现层(Representation)
“资源”是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的“表现层(Representation)”。
比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。
URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对“表现层”的描述。
(3)状态转化(State Transfer)
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化(State Transfer)”。而这种转化是建立在表现层之上的,所以就是“表现层状态转化”。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
综合上面的解释,我们总结一下什么是RESTful架构:
①每一个URI代表一种资源;
②客户端和服务器之间,传递这种资源的某种表现层;
③客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
1.2 举例理解Rest[2]
Leonard Richardson提出的Richardson Maturity Model如下图所示。
图1-1
参考文献[2]中作者风趣的讲述了什么是Rest。接下来,复述一下它的例子:
场景描述:去咖啡厅吃下午茶,到前台对服务员说“要一个芝士蛋糕,一杯拿铁,两条吸管,谢谢”。
(1)Level 0--面向前台
①我们将对前台说“点一杯拿铁”,将其转换成如下的Json表述:
{
“addorder”:{
“orderName”: “latte”
}
}
②前台回复订单:
{
“orderId”: “123456”
}
③向前台查询会员卡余额:
{
“queryBalance”:{
“cardId”: “886333”
}
}
④前台回复:
{
“balance”: “0”
}
⑤取消订单:
{
“deleteOrder”:{
“orderId”: “123456”
}
}
上述表达方式的缺陷:当订单越来越多时,单靠前台去处理显然不行;于是需要每个资源都有专人负责,顾客可以直接面向资源操作。
(2)Level 1--面向资源
仍然重复上面的下单过程:
①点一杯拿铁
/orders
{
“addorder”:{
“orderName”: “latte”
}
}
/orders表示我们这个请求发给哪个资源,订单是一种资源,可以理解为咖啡厅专门管理订单的人,他可以帮助我们处理所有的订单的操作,包括新增订单、修改订单、取消订单等操作。
②返回订单编号
{
“orderId”: “123456”
}
③查询会员卡余额
/cards
{
“queryBalance”:{
“cardId”: “886333”
}
}
此时,资源变成了cards。
④返回余额
{
“balance”: “0”
}
⑤取消订单
/orders
{
“deleteOrder”:{
“orderId”: “123456”
}
}
上述表述的缺陷:每次负责处理订单的员工,都需要到订单内容中看是新增还是删除订单,十分不方便。如果在所有新增资源的请求上都增加一个类似“POST”标签,就明确很多了。
(3)Level 2--打上标签
同样重复点餐的过程:
①点一杯拿铁
POST /orders
{
“orderName”: “latte”
}
②返回订单编号
{
“orderId”: “123456”
}
③查询会员卡余额
GET /cards
{
“cardId”: “886333”
}
进一步可以优化成:GET /cards/886333
④回复
{
“balance”: “0”
}
⑤取消订单
DELETE /orders
{
“orderId”: “123456”
}
进一步优化成:DELETE /orders/123456
(4)Level 3--完美服务
对于普通顾客而言,当它通过“POST /orders/{orderId}”点单后,它并不知道如何取消这个订单,于是在返回信息上,还需要做一些调整。
①点一杯拿铁
POST /orders
{
“orderName”: “latte”
}
②回复订单编号
{
“orderId”: “123456”
“link”:{
“rel”: “cancel”,
“url”: “/orders/123456”
}
}
返回信息中多了一项link信息,里面包含一个rel属性和url属性,rel是relationship的意思,这里的关系是cancel,url则告诉你如何执行这个cancel操作。
这一层的名字叫做HATEOAS(Hypertext As The Engine Of Application State),中文翻译为“将超媒体格式作为应用状态的引擎”。即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
1.3 Rest系统的特征[3]
(1)客户-服务器(Client-Server),提供服务的服务器和使用服务的客户需要被隔离对待。
(2)无状态(Stateless),来自客户的每一个请求必须包含服务器处理该请求所需的所有信息。换句话说,服务器端不能存储来自某个客户的某个请求中的信息,并在该客户的其他请求中使用。
(3)可缓存(Cachable),服务器必须让客户知道请求是否可以被缓存。(Ross:更详细解释请参考 理解本真的REST架构风格 以及 StackOverflow 的这个问题 中对缓存的解释。)
(4)分层系统(Layered System),服务器和客户之间的通信必须被这样标准化:允许服务器和客户之间的中间层(Ross:代理,网关等)可以代替服务器对客户的请求进行回应,而且这些对客户来说不需要特别支持。
(5)统一接口(Uniform Interface),客户和服务器之间通信的方法必须是统一化的。(Ross:GET,POST,PUT.DELETE, etc)
(6)支持按需代码(Code-On-Demand,可选),服务器可以提供一些代码或者脚本(Ross:Javascrpt,flash,etc)并在客户的运行环境中执行。这条准则是这些准则中唯一不必必须满足的一条。(Ross:比如客户可以在客户端下载脚本生成密码访问服务器。)
参考:
[1] 《理解RESTful架构》http://www.ruanyifeng.com/blog/2011/09/restful.html
[2] https://zhuanlan.zhihu.com/p/30396391?group_id=937244108725641216
[3] https://blog.youkuaiyun.com/qq_21383435/article/details/80032375