如何实现真正的 REST 风格?

  • Mark Little
  • 徐涵

2008 年 10 月 28 日

话题:SOAREST架构

SocialSite 的 REST API最近因Roy Fielding 称其不符合 REST 风格而受到批评。Roy 说,它是众多自称符合 REST 风格而实则不然的系统之一。

OpenSocial 的 REST API 是 RPC 式的,而且是公然宣誓其 RPC 本性。它在如此多的方面存在耦合,所以理应将它评为“差”。

从 OpenSocial 网页上提供的信息来看,你不难同意 Roy 的观点。例如:

  • 在服务端为 OpenSocial 风格的 REST 和 JSON-RPC 提供支持
  • 在客户端为 JSON-RPC 批量请求提供支持
  • 服从 OpenSocial 对扩展的需求

另外,我们都知道REST 跟 RPC 是紧密相关的。 鉴于已经见过很多自称符合 REST 风格而实则不然的网站,Roy 接着就如何构建真正符合 REST 风格的网站(及 API)给予了指导。现部分摘录如下:

  • REST API 不应依赖于某一个通信协议。如果一个协议元素(protocol element)要将 URI 用于标识的目的,那么它必须允许采用任意 URI 方案(scheme)。[做不到这一点,就意味着标识与交互没有分离。]
  • 媒体类型(media types)是用于表示资源和推进应用状态(application state)的,REST API 应将绝大部分描述精力用于定义媒体类型,或者是为现有的标准媒体类型定义扩展的关系名称(relation names)和 / 或基于超文本的标记(markup)。如果要就“对所谈及的 URIs 采用什么方法”进行定义的话,那么应完全把它放在媒体类型的处理规则 范围内进行定义(不过在大多数情况下,现有媒体类型都已经定义好了)。[做不到这一点,就意味着交互不是由超文本、而是由外部信息(out-of-band information)推进的。]
  • REST API 一定不能定义固定的资源名称或层次。服务器必须可以自由控制它自己的名称空间(namespace)。应该像 HTML 表单(HTML forms)和 URI 模版(URI templates)那样,通过媒体类型和链接关系(link relations)给出指示,使得服务器可以指导客户端如何构造正确的 URIs。[做不到这一点,就意味着客户端在根据外部信息(比如跟领域相关的标准)假定资源结构——相当于面向数据方法里的 RPC 功能耦合。]
  • 要使用 REST API,应该只需知道初始 URI(书签)和一套适合于目标用户(即可被任何使用该 API 的客户端所理解)的标准媒体类型。这样的话,所有的应用状态迁移, 都必须以“客户端在服务器提供的选项里挑选”这样的方式进行;服务器提供的选项,或者直接出现在用户收到的表示(representations)里,或 者在用户对那些表示进行处理后得到。客户端可以根据自己所掌握的关于媒体类型与资源通信机制的知识来决定(或限制)状态转移,客户端可以即时增加对媒体类 型与资源通信机制的支持(比如通过代码请求)。[做不到这一点,就意味着交互不是由超文本、而是由外部信息推进的。]

Roy 的这篇文章收到了很多反馈,有的是直接回复评论,有的是另发文章,其中有人提出了一些关于超文本 / 超媒体使用的问题,对此 Roy 回答道:

我所说的超文本(hypertext)指的是信息与控件的同时呈现,这样一来,信息便具有自解释性 (affordance)了,从而用户(或程序)可以通过它获取选项、并作出选择。超媒体(hypermedia)只是对文本的含义加以延伸、在媒体流里增 加了时间锚(temporal anchors);大部分研究者已经不对它们加以区分了。超文本不一定就是浏览器里的 HTML。机器只要理解数据格式和关系类型,它就可以跟随链接。

当被问及为什么他觉得很多人未能正确实现 REST 风格时,Roy 说:

某种程度上,人们未能正确实现 REST 风格,是因为我在博士论文里没有就媒体类型设计(media type design)作充分详细的论述。那并不是因为我觉得媒体 类型设计不如 REST 的其他方面重要,而是因为我当时时间不够。还有,我想很多人做得不对,可能是因为他们仅仅阅读了根据非权威资料撰写而成的 Wikipedia 相关条目。不过,我觉得很多人存在一个错误的认识,他们认为:设计简单的东西,应该是轻而易举的。而在现实中,设计某样东西需要花费的 精力,与结果的简单 程度是成反比的。与其他架构风格相比,REST 是相当简单的。REST 是用于长远考虑的软件设计:它的每一个细节都是为了提升软件寿命和独立演化。有许多 约 束是直接与短期功效对立的。不幸的是,人们较擅长于短期设计,而对待长期设计就很糟糕了。大部分人认为他们不需要为以后的版本作考虑。有不少软件方法都把 长远 考虑说成是执迷不悟的、象牙塔的设计(若不是有实际需求的话,那么可能是的)。

实际上,如果你对 REST 感兴趣的话,对该文的所有回复都值得一读。Dare Obasanjo 在一篇单独的文章中进行了概括总结

最要铭记的是,REST 所构建的,是在万维网(World Wide Web)上使用、对 Web 生态系统有利的软件。理想情况下,一个 REST 风格的 API 既可为众多网站所用、又可被运行在各种平台上的应用所用,而且客户 端应用与 Web 服务之间是零耦合的。RSS/Atom 提要(feed)就是一个很好的例子,它也是世界上最成功的 REST 式 API。

他专门考察了 Roy 提到了一种错误做法:实现 API 的服务需具有一种特定的 URI 结构。

这种做法的问题是,它假定每一个实现者都对他们的 URI 空间拥有完全控制权,而且客户端应该把 URL 结构写进代码里去。Joe Gregorio 在文章《No Fishing - or - Why 'robots.txt and 'favicon.ico' are bad ideas and shouldn't be emulated》里很好地解释了这一做法不好的原因,他在文章中列出了写死 URL 不好的几点理由,比如:缺乏扩展性;不支持那些采用寄存环境、从而无法控制 URI 空间的用户。

网上有大量其他 REST 资源(双关)。它们大部分由权威人士编写,并记录下了有关实现。不过显而易见的是,并不是所有使用 Web 的应用都是 REST 式应用,也并不是所有自称符合 REST 风格的应用都符合。

查看英文原文:What Makes Good REST?

SOAREST架构