restful架构风格设计准则(三)资源识别和资源设计

读书笔记,原文链接:http://www.cnblogs.com/loveis715/p/4669091.html,感谢作者!

restful风格的设计中,首先要识别系统中的资源,然后用HTTP规范表示这些资源。

一、资源识别

1、以资源为中心 vs 以动作为中心

1.1、以动作为中心

在传统的软件分析设计中,常常要分析业务要使用的业务逻辑,并为业务逻辑的执行提供一系列接口。如:将商品放入购物车,提交订单等。这一系列接口组合在一起就可以组成完成目标所需要执行的业务逻辑。在需要调用这些接口的时候,软件开发人员需要向这些接口所在的URL发送一个请求,从而驱使服务执行该动作。

1.2、以资源为中心

在restful风格的软件分析设计中,我们所提供的各个接口则需要是一系列资源,而业务逻辑需要通过对资源的操作来完成。REST服务中的API将不再以执行了什么动作为中心,而是以资源为中心。一些对资源的通用操作有添加,修改,删除, 获取,以及对符合特定条件的资源进行列表操作。 

2、资源候选者

2.1、找到主资源

资源候选者:动作的宾语,看能否独立

我们再以“将商品放入购物车”这个操作为例。

在一个REST系统中,购物车将被抽象为一个资源,而“将商品放入购物车”这个操作将被解释为对购物车这个资源的更新:更新购物车,以使特定商品包含在购物车内。

这种描述方法的不再以动作为中心,而是以资源为中心。与之对应的是系统设计步骤的改变:我们将不再首先是别完成业务逻辑所需的各动作,而是支持业务逻辑所需要的各资源。

那么我们应该如何抽象出这些资源呢?首先,我们对某个操作不要再关注它所执行的动作,而是关心它所操作的宾语。通常情况下,该宾语就会是REST系统中的资源。 

资源候选者:资源包含的信息,如果被系统中其他资源使用,就可能是潜在的资源

在这里,我们就以“提交订单”作为示例来展示如何抽象资源。

首先,在“提交订单”这个动作中,订单是宾语。因此对于该业务逻辑,其将作为一个资源存在。

除此之外,在订单中还需要包含一系列信息,例如订单中所包含的商品,订单所属人等。一旦这些都可以被该REST系统中的其它资源使用,那么它们也将成为独立的资源。 

资源候选者:变化的实体

有时候一个动作可能并不存在着它所操作的宾语。在这种情况下,我们就需要考虑该动作产生或消除了哪个实体,或者哪个实体的状态发生了变化。这个发生了变化的实体实际上就是一种资源。

例如对于登陆这一行为,其实际上在服务端创建了一个会话实例。该会话实例中则包含了登陆IP,登陆时间,以及登陆时所用的凭证等。再比如对于用户更改密码这种行为,其所操作的资源就是用户资料。

2.2、依次识别子资源 

找到主资源

在抽象资源的过程中,我们需要按照自顶向下的方式,即首先辨识出系统中的最主要资源,然后再辨识这些主要资源的子资源,并依次进行迭代。

对主资源的抽取主要通过分析业务逻辑来完成。在得到功能需求以后,我们首先要分析这些业务逻辑所操作的宾语。

这些宾语可能有两种情况:主资源或者其它资源的子资源。主资源实际上就是能够独立存在的一系列资源。而子资源则需要依附于主资源之上才能表达实际的意义。同时各个子资源也可能拥有自身的子资源。 

依次识别子资源

判断一个资源是否是子资源的一个方法就是看它是否能独立地表示其具体含义。例如对于一个egoods上所销售的商品,其名称,价格,简介等属性可以清晰地描述该商品到底是什么,到底如何销售。因此这些商品实际上是一个主资源。但是每种商品所支持的邮递服务需要是一个子资源:一个商品可以支持多种邮递服务。这些邮递服务根据派送距离等需要不同的价格,也提供了不同的邮递速度。由于这些邮递服务与商家和邮递服务公司所达成的服务价格有关,并且会由于商品重量的变化而变化,因此这些邮递服务并不能为其它商家所提供的邮递服务作为参考,因此其应该作为该商品的一个子资源。

或者也可以说,如果一个资源是主资源,那么其可以被不同的资源实例包含引用而不会产生歧义。而如果一个资源是子资源,那么被不同的资源实例引用可能会产生歧义。

2.3、如何判断为REST服务所定义的资源是否合理?

  • 检查对该资源的CRUD是否有意义
  • 检查资源是否需要除CRUD之外的动词来操作。该方法用来检查资源中是否还有子资源没有被抽象。如果该资源还需要额外的动词,那么我们要考虑这些操作到底引起了什么样的状态变化,进而抽象出该资源的子资源。
  • 检查这些资源是否是被整体使用,创建和删除。该方法用来探测是否一个子资源应该是一个主资源。如果在删除一个资源时,其子资源还可以被其它资源重用,那么该子资源实际上具有较高的重用性,应该是一个主资源。

二、资源表示

每个资源都拥有一个资源标识,所以正确识别一个资源之后,我们就要为这些资源分配其所对应的URI。

在HTTP中,一个URL主要由四部分组成:

  • 协议:http https
  • 主机名和端口:www.taobao.com:8080
  • 资源的相对路径:/API/categories
  • 请求参数:即由问号开始的由键值对组成的字符串:?page=1&page_size=20

通过URL来表示资源: 在识别出REST系统中的各个资源后,我们需要为这些资源设计各自所对应的URL。

1、所有的资源都应该存在于一个相对路径之下

GET /API
Host: www.xxx.com
Authorization: Basic xxxxxxxxxxxxxxxxxxx
Accept: application/json

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: xxx

{
"version": "1.0",
"resources": [
{
"label" : "Categories",
"description" : "Product categories",
"uri": "/API/categories"
}, {
"label" : "Items",
"description" : "All items on sell",
"uri": "/API/items"
}
]
}
因此对于从向该相对路径发送请求才能得到的各个主资源来说,将它们置于相对路径/API之下是非常合理的。

除了这个原因之外,API的版本更迭也是一个考虑。假如软件开发人员需要开发一个新版本的REST API,那么他可能就需要重新抽象并定义系统中的各个资源。

但是如果两个版本的API中都拥有一个categories资源,并且系统为了保持后向兼容性同时保留了两个版本的API,那么将只有一个资源可以使用/categories这个相对路径。

正因为如此,将这些资源置于相对路径/API之下,并在第二个版本的API出现之后将新的资源抽象置于/API-v2下是一种较为流行的做法。

2、主资源所对应的URL

主资源是一类独立的资源,因此它应该直接置于/API下。

例如:

  • xxx网站中的产品分类是一个主资源,我们可以为其分配URL:/API/categories。
  • xxx网站中的产品也是一个主资源,我们可以为其分配URL:/API/items

这样,每类主资源都将拥有一个特定于该类资源的URL。这些URL就对应着相应资源实例的集合。

3、主资源类型的特定实例的URL=该类主资源所对应URL后添加该实例ID

例如:xxx网站中的食品分类的ID为1,其对应URL:/API/categories/1

对于某种类型的主资源,整个系统将有且仅有一个该类型资源的实例,那么该资源实例将不需要通过ID来访问。如:对整个系统进行介绍的资源,该资源实例所对应的URL:/API/about

4、主资源实例下的子资源的URL

一个资源实例中还可能拥有子资源。这些子资源与资源实例之间的关系主要有两种情况:

  • 资源实例包含了一个子资源的集合,如:对于ID为23456的商品所提供的邮递服务,使用URL:/API/items/23456/shipments,其中一个ID为87256的邮递服务所对应的URI则为:/API/items/23456/shipments/87256
  • 资源实例仅仅可以包含一个子资源,如:当前商品的折扣信息:/API/items/23456/discount
原文地址:https://www.cnblogs.com/lexiaofei/p/7567834.html