《我所理解的RESTful WebAPI [Web标准篇]》Web服务已经成为了异质系统之间的互联与集成的主要手段,在过去一段不短的时间里,Web服务几乎清一水地采用SOAP来构建。构建REST风格的Web服务是最近两三年风行的潮流,所以很多人以为REST是一个事物。而事实却是:REST自其诞生之日起到现在(2014年)已经有14年了,它为什么叫这么一个“奇怪”的名字呢?
目录
一、为什么叫这个“奇怪”的名字?
二、采用URI标识资源
三、使用“链接”关联相关的资源
四、使用统一的接口
五、使用标准的HTTP方法
六、支持多种资源表示方式
七、无状态性
一、为什么叫这个“奇怪”的名字?
2000年,Roy ThomasFielding博士在他那篇著名的博士论文《Architectural Styles and the Design of Network-basedSoftwareArchitectures》中提出了几种软件应用的架构风格,REST作为其中的一种架构风格在这篇论文的第5章中进行了概括性的介绍。我个人建议本书的读者都能读读这篇论文,原文和中文译文都可以从网络上找到。
REST是“REpresentationalStateTransfer”的缩写,可以翻译成“表现状态转换”,但是在绝大多数场合中我们只说REST或者RESTful。为什么会起这么一个奇怪的名字呢?我们可以从上述这篇论文中找到答案。Fielding在论文中将REST定位为“分布式超媒体应用(DistributedHypermedia System)”的架构风格,它在文中提到一个名为“HATEOAS(Hypermediaas the engine of application state)”的概念。
我们利用一个面向最终用户的Web应用来对这个概念进行简单阐述:这里所谓的应用状态(ApplicationState)表示Web应用的客户端的状态,简单起见可以理解为会话状态。资源在浏览器中以超媒体的形式呈现,通过点击超媒体中的链接可以获取其它相关的资源或者对当前资源进行相应的处理,获取的资源或者针对资源处理的响应同样以超媒体的形式再次呈现在浏览器上。由此可见,超媒体成为了驱动客户端会话状态的转换的引擎。
借助于超媒体这种特殊的资源呈现方式,应用状态的转换体现为浏览器中呈现资源的转换。如果将超媒体进一步抽象成一般意义上的资源呈现(Representation)方式,那么应用状态变成了可被呈现的状态(REpresentational State)。应用状态之间的转换就成了可被呈现的状态装换(REpresentational State Transfer),这就是REST。
REST在我看来是一种很笼统的概念,它代表一种架构风格。对于多个Web应用采用的架构,我们只能说其中某一个比其它的更具有REST风格,而不能简单粗暴地说:“它采用了REST架构而其它的没有”。为了将REST真正地落地,LenoardRechardson & Sam Ruby在《RESTful Web Services》一书中提出了一种名为“面向资源的架构(ROA:Resource Oriented Architecture)”。该书中介绍了一些采用ROA架构的Web服务应该具备的基本特征,它们可以指导我们如果构架具体的RESTfulWeb API。
二、采用URI标识资源
SOAP WebAPI采用RPC风格,它采用面向功能的架构,所以我们在设计SOAP Web API的时候首相考虑的是应高提供怎样的功能(或者操作)。RESTful WebAPI采用面向资源的架构,所以在设计之初首先需要考虑的是有哪些资源可供操作。
资源是一个很宽泛的概念,任何寄宿于Web可供操作的“事物”均可视为资源。资源可以体现为经过持久化处理保存到磁盘上的某个文件或者数据库中某个表的某条记录,也可以是Web应用接受到请求后采用某种算法计算得出的结果。资源可以体现为一个具体的物理对象,它也可以是一个抽象的流程。
一个资源必须具有一个或者多个标识,既然我们设计的WebAPI,那么很自然地应该采用URI来作为资源的标识。作为资源标识的URI最好具有“可读性”,因为具有可读性的URI更容易被使用,使用者一看就知道被标识的是何种资源,比如如下一些URI就具有很好的可读性。
http://www.artech.com/employees/c001(编号C001的员工)
http://www.artech.com/sales/2013/12/31(2013年12月31日的销售额)
http://www.artech.com/orders/2013/q4(2013年第4季度签订的订单)
除了必要的标志性和可选的可读性之外,标识资源的URI应该具有“可寻址性(Addres****lity)”。也就是说,URI不仅仅指明了被标识资源所在的位置,而且通过这个URI可以直接获取目标资源。通过前面的介绍我们知道URI具有URL和URN两种主要的表现形式,只要前者具有可寻址性,所以我们最好采用一个URL作为资源的标识。
URI除了可以标识某个独立的资源外(比如“http://www.artech.com/employees/c001”),还可以标识一组资源的集合或者资源的容器(比如“http://www.artech.com/orders/2013/q4”)。当然,一组同类资源的集合或者存放一组同类资源的容器本身也可以视为另一种类型的复合型(Composite)资源,所以“URI总是标识某个资源”这种说法是没有问题的。
三、使用“链接”关联相关的资源
在绝大多数情况下,资源并不会孤立地存在,必然与其它资源具有某种关联。既然我们推荐资源采用具有可寻址性的URL来标识,那么我们就可以利用它来将相关的资源关联起来。比如我们采用XML来表示一部电影的信息,那么我们采用如下的形式利用URL将相关的资源(导演、领衔主演、主演、编剧以及海报)关联在一起。实际上这可以视为一份超文本/超媒体文档。当用户得到这样一份文档的时候,可以利用自身的内容获得某部影片基本的信息,还可以利用相关的“链接”得到其它相关内容的详细信息。
1: <movie>
2: <name>魔鬼代言人</name>
3: <genre>剧情|悬疑|惊悚</genre>
4: <directors>
5: <addref="http://www.artech.com/directors/taylor-hackford">泰勒.海克福德</add>
6: </directors>
7: <starring>
8: <addref = "http://www.artech.com/actors/al-pacino">阿尔.帕西诺</add>
9: <addref = "http://www.artech.com/actors/keanu-reeves ">基诺.李维斯</add>
10: </starring>
11: <supportingActors>
12: <addref = "http://www.artech.com/actors/charlize-theron ">查理兹.塞隆</add>
13: <addref = "http://www.artech.com/actors/jeffrey-jones ">杰弗瑞.琼斯</add>
14: <addref = "http://www.artech.com/actors/connie-nielsen">康尼.尼尔森</add>
15: </supportingActors>
16: <scriptWriters>
17: <addref = "http://www.artech.com/scriptwriters/jonathan-lemkin">乔纳森•莱姆金</add>
19: <addref = "http://www.artech.com/scriptwriters/tony-gilroy">托尼•吉尔罗伊 </add>
20: </scriptWriters>
21: <language>英语</language>
22: <poster ref = "http://www.artech.com/images/the-devil-s-advocate"/>
23: <story>...</story>
24: </movie>
Fielding在他的论文中将REST定位为“分布式超媒体应用”的架构风格,而超媒体的核心就是利用“链接”相关的信息结成一个非线性的网,所以从一点也可以看出REST和“使用链接关联相关的资源”这个特性使吻合的。
四、使用统一的接口
由于REST是面向资源的,所以一个WebAPI旨在实现针对单一资源的操作。我们在前面已经说个,针对资源的基本操作唯CRUD而已,这是使我们可以为WebAPI定义标准接口成可能。所谓的标准接口就是针对不同资源的Web API定义一致性的操作来操作它们,其接口可以采用类似于下面的模式。
1: public classResourceService
2: {
3: publicIEnumerable<Resource>[] Get();
4: public void Create(Resourceresource);
5: public void Update(Resourceresource);
6: public void Delete(string id);
7: }
能否采用统一接口是RESTful WebAPI和采用RPC风格的SOAP Web服务又一区别。如果采用RPC风格的话,我们在设计WebAPI的时候首先考虑的是具体哪些功能需要被提供,所以这样的Web API是一组相关功能的集合而已。
以一个具体的场景为例。现在我们需要设计一个WebAPI来管理用于授权的角色,它只需要提供针对角色本身的CRUD的功能以及建立/解除与用户名之间的映射关系。如果我们将其定义成针对SOAP的Web服务,其服务接口具有类似于如下的结构。
1: public class RoleService
2: {
3: public IEnumerable<string> GetAllRoles();
4: public void CreateRole(string roleName);
5: public void DeleteRole(string roleName);
6:
7: public void AddRolesInUser(string userName, string[] roleNames);
8: public voidRemoveRolesFromUser(string userName, string[] roleNames);
9: }
如下我们需要将其定义成一个纯粹的RESTful的WebAPI,只有前面三个方法在针对角色的CRUD操作范畴之内,但是后面两个方法却可以视为针对“角色委派(RoleAssignment)”对象的添加和删除操作。所以这里实际上涉及到了两种资源,即角色和角色委派。为了使WebAPI具有统一的接口,我们需要定义如下两个Web API。
1: public class RolesService
2: {
3: public IEnumerable<string> Get();
4: public void Create(string roleName);
5: public void Delete(string roleName);
6: }
7:
8: public classRoleAssignmentsService
9: {
10: public voidCreate(RoleAssignment roleName);
11: public voidDelete(RoleAssignment roleName);
12: }
目录
一、为什么叫这个“奇怪”的名字?
二、采用URI标识资源
三、使用“链接”关联相关的资源
四、使用统一的接口
五、使用标准的HTTP方法
六、支持多种资源表示方式
七、无状态性
一、为什么叫这个“奇怪”的名字?
2000年,Roy ThomasFielding博士在他那篇著名的博士论文《Architectural Styles and the Design of Network-basedSoftwareArchitectures》中提出了几种软件应用的架构风格,REST作为其中的一种架构风格在这篇论文的第5章中进行了概括性的介绍。我个人建议本书的读者都能读读这篇论文,原文和中文译文都可以从网络上找到。
REST是“REpresentationalStateTransfer”的缩写,可以翻译成“表现状态转换”,但是在绝大多数场合中我们只说REST或者RESTful。为什么会起这么一个奇怪的名字呢?我们可以从上述这篇论文中找到答案。Fielding在论文中将REST定位为“分布式超媒体应用(DistributedHypermedia System)”的架构风格,它在文中提到一个名为“HATEOAS(Hypermediaas the engine of application state)”的概念。
我们利用一个面向最终用户的Web应用来对这个概念进行简单阐述:这里所谓的应用状态(ApplicationState)表示Web应用的客户端的状态,简单起见可以理解为会话状态。资源在浏览器中以超媒体的形式呈现,通过点击超媒体中的链接可以获取其它相关的资源或者对当前资源进行相应的处理,获取的资源或者针对资源处理的响应同样以超媒体的形式再次呈现在浏览器上。由此可见,超媒体成为了驱动客户端会话状态的转换的引擎。
借助于超媒体这种特殊的资源呈现方式,应用状态的转换体现为浏览器中呈现资源的转换。如果将超媒体进一步抽象成一般意义上的资源呈现(Representation)方式,那么应用状态变成了可被呈现的状态(REpresentational State)。应用状态之间的转换就成了可被呈现的状态装换(REpresentational State Transfer),这就是REST。
REST在我看来是一种很笼统的概念,它代表一种架构风格。对于多个Web应用采用的架构,我们只能说其中某一个比其它的更具有REST风格,而不能简单粗暴地说:“它采用了REST架构而其它的没有”。为了将REST真正地落地,LenoardRechardson & Sam Ruby在《RESTful Web Services》一书中提出了一种名为“面向资源的架构(ROA:Resource Oriented Architecture)”。该书中介绍了一些采用ROA架构的Web服务应该具备的基本特征,它们可以指导我们如果构架具体的RESTfulWeb API。
二、采用URI标识资源
SOAP WebAPI采用RPC风格,它采用面向功能的架构,所以我们在设计SOAP Web API的时候首相考虑的是应高提供怎样的功能(或者操作)。RESTful WebAPI采用面向资源的架构,所以在设计之初首先需要考虑的是有哪些资源可供操作。
资源是一个很宽泛的概念,任何寄宿于Web可供操作的“事物”均可视为资源。资源可以体现为经过持久化处理保存到磁盘上的某个文件或者数据库中某个表的某条记录,也可以是Web应用接受到请求后采用某种算法计算得出的结果。资源可以体现为一个具体的物理对象,它也可以是一个抽象的流程。
一个资源必须具有一个或者多个标识,既然我们设计的WebAPI,那么很自然地应该采用URI来作为资源的标识。作为资源标识的URI最好具有“可读性”,因为具有可读性的URI更容易被使用,使用者一看就知道被标识的是何种资源,比如如下一些URI就具有很好的可读性。
http://www.artech.com/employees/c001(编号C001的员工)
http://www.artech.com/sales/2013/12/31(2013年12月31日的销售额)
http://www.artech.com/orders/2013/q4(2013年第4季度签订的订单)
除了必要的标志性和可选的可读性之外,标识资源的URI应该具有“可寻址性(Addres****lity)”。也就是说,URI不仅仅指明了被标识资源所在的位置,而且通过这个URI可以直接获取目标资源。通过前面的介绍我们知道URI具有URL和URN两种主要的表现形式,只要前者具有可寻址性,所以我们最好采用一个URL作为资源的标识。
URI除了可以标识某个独立的资源外(比如“http://www.artech.com/employees/c001”),还可以标识一组资源的集合或者资源的容器(比如“http://www.artech.com/orders/2013/q4”)。当然,一组同类资源的集合或者存放一组同类资源的容器本身也可以视为另一种类型的复合型(Composite)资源,所以“URI总是标识某个资源”这种说法是没有问题的。
三、使用“链接”关联相关的资源
在绝大多数情况下,资源并不会孤立地存在,必然与其它资源具有某种关联。既然我们推荐资源采用具有可寻址性的URL来标识,那么我们就可以利用它来将相关的资源关联起来。比如我们采用XML来表示一部电影的信息,那么我们采用如下的形式利用URL将相关的资源(导演、领衔主演、主演、编剧以及海报)关联在一起。实际上这可以视为一份超文本/超媒体文档。当用户得到这样一份文档的时候,可以利用自身的内容获得某部影片基本的信息,还可以利用相关的“链接”得到其它相关内容的详细信息。
1: <movie>
2: <name>魔鬼代言人</name>
3: <genre>剧情|悬疑|惊悚</genre>
4: <directors>
5: <addref="http://www.artech.com/directors/taylor-hackford">泰勒.海克福德</add>
6: </directors>
7: <starring>
8: <addref = "http://www.artech.com/actors/al-pacino">阿尔.帕西诺</add>
9: <addref = "http://www.artech.com/actors/keanu-reeves ">基诺.李维斯</add>
10: </starring>
11: <supportingActors>
12: <addref = "http://www.artech.com/actors/charlize-theron ">查理兹.塞隆</add>
13: <addref = "http://www.artech.com/actors/jeffrey-jones ">杰弗瑞.琼斯</add>
14: <addref = "http://www.artech.com/actors/connie-nielsen">康尼.尼尔森</add>
15: </supportingActors>
16: <scriptWriters>
17: <addref = "http://www.artech.com/scriptwriters/jonathan-lemkin">乔纳森•莱姆金</add>
19: <addref = "http://www.artech.com/scriptwriters/tony-gilroy">托尼•吉尔罗伊 </add>
20: </scriptWriters>
21: <language>英语</language>
22: <poster ref = "http://www.artech.com/images/the-devil-s-advocate"/>
23: <story>...</story>
24: </movie>
Fielding在他的论文中将REST定位为“分布式超媒体应用”的架构风格,而超媒体的核心就是利用“链接”相关的信息结成一个非线性的网,所以从一点也可以看出REST和“使用链接关联相关的资源”这个特性使吻合的。
四、使用统一的接口
由于REST是面向资源的,所以一个WebAPI旨在实现针对单一资源的操作。我们在前面已经说个,针对资源的基本操作唯CRUD而已,这是使我们可以为WebAPI定义标准接口成可能。所谓的标准接口就是针对不同资源的Web API定义一致性的操作来操作它们,其接口可以采用类似于下面的模式。
1: public classResourceService
2: {
3: publicIEnumerable<Resource>[] Get();
4: public void Create(Resourceresource);
5: public void Update(Resourceresource);
6: public void Delete(string id);
7: }
能否采用统一接口是RESTful WebAPI和采用RPC风格的SOAP Web服务又一区别。如果采用RPC风格的话,我们在设计WebAPI的时候首先考虑的是具体哪些功能需要被提供,所以这样的Web API是一组相关功能的集合而已。
以一个具体的场景为例。现在我们需要设计一个WebAPI来管理用于授权的角色,它只需要提供针对角色本身的CRUD的功能以及建立/解除与用户名之间的映射关系。如果我们将其定义成针对SOAP的Web服务,其服务接口具有类似于如下的结构。
1: public class RoleService
2: {
3: public IEnumerable<string> GetAllRoles();
4: public void CreateRole(string roleName);
5: public void DeleteRole(string roleName);
6:
7: public void AddRolesInUser(string userName, string[] roleNames);
8: public voidRemoveRolesFromUser(string userName, string[] roleNames);
9: }
如下我们需要将其定义成一个纯粹的RESTful的WebAPI,只有前面三个方法在针对角色的CRUD操作范畴之内,但是后面两个方法却可以视为针对“角色委派(RoleAssignment)”对象的添加和删除操作。所以这里实际上涉及到了两种资源,即角色和角色委派。为了使WebAPI具有统一的接口,我们需要定义如下两个Web API。
1: public class RolesService
2: {
3: public IEnumerable<string> Get();
4: public void Create(string roleName);
5: public void Delete(string roleName);
6: }
7:
8: public classRoleAssignmentsService
9: {
10: public voidCreate(RoleAssignment roleName);
11: public voidDelete(RoleAssignment roleName);
12: }









