内容


面向资源与面向活动的 Web 服务

REST 样式与 SOAP 样式 Web 服务之间关系的概览

Comments

每当一些 Web 应用服务提供方提出允许开发者集成他们的服务的 Web 服务 API 时,大家都非常关心由 API 实现的互操作设计模式。如果 API 使用的是 REST 样式的互操作,REST 方法的拥护者就会将该 API 作为说明为什么 REST 样式服务比 SOAP 样式服务更优越的重要例子而加以称赞;同样地,如果 API 使用 SOAP 样式 Web 服务模式,情况也类似。似乎很少有人关心这样的一个事实,模式的选择主要取决于正在被执行的应用程序的类型,并且像所有优秀的体系结构决策一样,开发者应该将他们的选择基于正在被开发的应用程序的特定技术需求和特性,而不是基于针对单一体系结构方法的一些特殊偏好。

资源还是活动?

从基本原理层次上说,REST 样式和 SOAP 样式 Web 服务的区别取决于应用程序是面向 资源的还是面向 活动的。

面向资源服务集中于明确的数据对象,一些基本、标准的操作可以依据这些数据对象而执行。如权威的 Gang of Four(GoF) 设计模式这本书所述,对于熟悉面向对象设计模式概念的开发者来说,面向资源服务与基本 Memento 模式类似。实际上,服务提供方维护一组资源,并且公开一组基本操作来执行以下任务:

  • 检索资源
  • 修改资源
  • 创建新资源
  • 删除资源

根据定义,REST 样式 Web 服务是面向资源的服务。您可以通过统一资源标识符(Universal Resource Identifier,URI)来识别和定位资源,并且针对这些资源而执行的操作是通过 HTTP 规范定义的。其核心操作包括:

  • GET - 该操作返回已标识资源的状态表示。您可以通过大量的上下文要素来确定状态,例如谁正在提交请求、操作的参数(传入的参数如 HTTP 头或者查询字符串参数)和服务提供方维护的当前会话状态。
  • POST - 该操作执行对已标识资源的一些特定于应用程序形式的更新。该操作行为完全依赖于实现它的服务。由该操作返回的数据也完全依赖于应用程序。举例来说,像 GET 操作一样,它可以返回一个状态表示,它还可以选择根本不返回任何数据。
  • PUT - 该操作在已标识位置(URI)创建新资源。操作输入必须包括一个资源的状态表示。它完全依赖服务来创建基于这个状态表示的资源。
  • DELETE - DELETE 操作销毁已标识位置(URI)的资源。

在许多方面,REST 样式 Web 服务与 SQL、元组空间(tuple spaces)、简单消息列队等技术相似。它们都使用普通的简单操作针对明确的资源起作用。

  • SQL - SELECT、INSERT、DELETE、UPDATE 等
  • 元组空间 - GET、PUT
  • 消息列队 - SEND、RECEIVE

在每一个案例中,服务接口的设计允许您移动关于资源的信息,让其依赖于请求方来指出希望通过这些信息来做什么。

与此相对的是 面向活动的资源。该类型的应用程序集中于您可能执行的操作,而不是集中于操作所依靠的资源。活动服务的一个简单的例子就是银行事务,在那里用户可以把钱从一个账户转移到另一个账户上。用户不想直接操作资源(钱、银行账户等等),他们只想告诉银行他们想要达到的目的,并且让银行根据他们的利益对资源进行处理。用 GoF 术语来描述应用程序:

  • 命令
  • 中介方
  • 策略
  • 代理设计模式

面向资源服务不管资源的类型怎样,执行的操作可以保持相对不变,与面向资源服务不同,面向活动服务的操作完全依赖于正在执行的活动类型。例如,银行服务可以公开一个名为 transferFunds 的操作,该操作不同的输入将完全决定服务的资金转移功能。

在面向资源的服务中,一组普通操作担当支持性的工作角色,为客户端提供访问和操作资源。然而,资源是关注的中心,如下面 图 1 所示。

图 1. 面向资源服务与面向活动服务的比较
图 1. 面向资源服务与面向活动服务的比较
图 1. 面向资源服务与面向活动服务的比较

在面向活动服务中,对客户端请求执行的每个活动的单一操作来说,操作是关注的中心。

SOAP 样式 Web 服务通常是面向活动的。 WSDL 文档定义并描述特定于服务的操作。操作由特定于服务的消息交换组成。每一个操作都是一个可以执行的活动。那些正在被执行的操作所针对的内容通常是不相关的。正如 Web 服务资源框架系列规范所描述的,资源可以隐含在活动之中,但是这种隐含与活动的定义不相关,并且只是为了改进执行活动所依赖的上下文。与针对资源而执行活动的面向资源服务相比,它和用来访问资源的服务接口互不相关。

结合上下文:Bloglines 资源

Bloglines 是一个基于 Web 的应用服务,它允许个人对 weblog 和新闻的各种订阅保持跟踪,这些订阅内容以 Really Simple Syndication(RSS)和 Atom 提供的形式交付。

Bloglines 服务根本上是面向资源的。用户创建、更新、和删除订阅,并且定期访问服务来了解自上次访问站点后发生了哪些更新。Bloglines API 利用 REST 样式 Web 服务接口适当地反映了面向资源的特性。

特别地,对于 Bloglines 来说重要的是订阅的用户集合,如众所周知的 blogroll。单个 blogroll 会由许多不同的订阅组成。一个 订阅指向一个 RSS 或者一个 Atom 提供。

Blogrolls 和订阅都是资源。Bloglines API 使用基本 HTTP 操作来检索关于用户的 blogroll 和订阅资源的当前状态信息。

例如,要访问用户 blogroll 的当前快照,用户向 URI //rpc.bloglines.com/listsubs 发送一个 HTTP GET 请求。在严格的 REST 条件下,该 API 确定了每一个 Bloglines 用户的订阅。当调用这个操作时,HTTP 身份验证请求收集关于检索哪个用户订阅的附加上下文。由 XML 文档组成的操作返回的信息为已验证用户列出所有的个人订阅。

为访问特定订阅的当前快照,用户对 URI http://rpc.bloglines.com/getitems?s={subid} 发送一个 HTTP GET 请求,如上述 listsubs 操作的结果中所展示的, {subid} 是 Bloglines 分配的唯一订阅标志符。定义附加参数,包括(非常有趣)一个实际上违反了基本 REST 规则的参数,也就是说,它们不应该引起资源状态中的任何变化。当被提供时,参数 (n=1) 指示 Bloglines 服务将以前未读的订阅条目标记为正在读取,这实际上改变了资源状态。

要返回当前用户 blogroll 中未读的订阅条目的数量,用户可以向 URI http://rpc.bloglines.com/update?user={email}&ver=1 发送一个 HTTP GET 请求,这里 {email} 是用户的 email 地址,这些用户的账户应该被检查,以获得未读的订阅条目。

Bloglines API 参与的唯一一件事是允许有计划的访问 blogrolls 和订阅资源。API 不关心客户端访问 blogrolls 或订阅时会如何处理它们。

提供一个对照,与 IBM 最近实现的概念验证工程做比较来阐明 Web 服务在自动化医院患者纪录的访问、处方的编写、手术的安排等方面的用途。不做详细的说明(与客户端的非公开协议涉及的内容),为系统建立的服务接口的集合完全集中于用户在系统中执行的活动。例如,为患者安排手续或检查,并请求检查结果的有效性等事件的异步通知。患者的档案(资源)是系统的一个很重要的方面,它通常在全部的应用程序中起到支持的作用,但不是系统的主要焦点。更确切地说,医院原型是一个面向活动的服务,这由它的 SOAP 样式 Web 服务体系结构确切的反映出来。

要点很简单:REST 和 SOAP 的选择归结为对您的特定应用程序的最重要部分的理解。如果您的应用程序主要集中在访问信息资源的能力(如 Bloglines 服务),那么您用的主要是面向资源服务,并且您的应用程序应该是 REST 样式的设计模式。这里应该优先考虑 Amazon、del.icio.us、Flickr 还有其他的一些厂商(请参阅 参考资料)提供的服务 API。然而,如果您的应用程序主要集中于被执行的活动(这些活动与所依赖的资源不相关),那么您的服务是面向活动的,并且应该利用 SOAP 样式的设计模式。

功能与形式

基于 REST 的模式的支持者常常指出它的体系结构化的简单性,并将其和 SOAP 型的 Web 服务规范的复杂性相比较,以此来说明为什么 REST 是更优越的方法。正如我已经阐明的,这一论断是有缺陷的,这两种方法要解决不同类型的问题。基于 Web 的应用程序服务,例如 Bloglines、Amazon、flikr、del.icio.us 等等,要面对不同类型的问题,这不是说说那么简单。医院需要将定制规程、编写处方的内部流程自动化,因此要求一个不同的体系结构化方法。这就是设计模式存在的原因 -- 不同的方法解决不同的问题。

在特定业务需求上下文之外进行比较,REST 体系结构表面上好像远不及该行业提出的各种 WS-* 规范所定义的 SOAP Web 服务体系结构复杂。而且,在某些关键地方它的功能也远没有那么强大。可靠消息是一个主要的例子。HTTP 不是一个可靠协议。这里没有使用一次且仅一次(once-and-only-once)或者重试直至成功(retry-until-success)的语义以允许可靠传输 HTTP 请求的机制。REST/HTTP 中也没有在基本 HTTP 代理机制之外进行消息路由的机制。在 REST 服务中的安全性上下文也通常局限于 HTTP 协议提供的安全机制,除非应用程序选择采用一些外部定义的安全机制(例如,一个 REST 服务实际上能返回数字签名或者加密 SOAP 消息来响应 HTTP GET 请求)。

对于如 Bloglines 之类的服务,这些功能限制不是关键,因为由 HTTP 提供的基本选项足以满足应用程序的需要。API 不需要:

  • 可靠消息传递
  • 数字签名
  • 消息路由
  • 资源生命周期管理
  • 异步事件通知
  • 由 SOAP WS-* 规范引进的其他性能

这并不意味着其他应用程序不要求由基于活动的方法所提供的增强功能。

不要理解错我的意思,所有这些在 REST 样式服务中做的事情至少从理论上讲是完全可行的。关键的问题是没有人定义过这样做的一致的标准方法。既然如此,举例来说,任何人使用任何方法在 REST 样式接口中做这样的工作(如异步事件通知或可靠消息传输),都将会是特定于实现的一次性服务,通常不会被其它 REST 样式的服务支持。然而需要指出的更重要的问题是,通常与适合于面向资源的服务相比,这些扩展的功能更适合于面向活动的服务,因此在面向活动的 SOAP 样式服务领域里,我们见到更多的规范和更大的复杂性也就不奇怪了。

注意:如果处理适当,复杂性并不是一件坏事。是的,有很多 WS-* 规范,涉及了很大范围的技术主题。从总体上看,它们代表了更高的复杂性。然而,它们定义的方法包含这样的概念,即仅仅提取对应用程序必要的一部分规范。没有人会期待每一个 Web 服务都能执行全部的 WS-* 规范。

概要:关于服务

最后,行业继续朝着面向服务体系结构方向前进,这是非常重要的。做出 REST 样式和 SOAP 样式的选择,应该与为给定应用程序组件实现 命令、中介方、观测器、策略或是访问者设计模式之间进行选择一样谨慎。简单地说,那只不过是一个基于业务和应用需要的设计策略,但却会严重影响您的应用程序的使用和将来的发展。然而,比 Web 服务设计模式的选择更为重要的是提供 Web 服务的选择。无论您选择实现哪种样式的服务,这都是非常关键的。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services
ArticleID=284461
ArticleTitle=面向资源与面向活动的 Web 服务
publish-date=11012004