“你总提及的那个词,它的含义与你想表达的意思并不一样。”
前言
眼下,在技术圈,REST俨然已经成为一个时髦的流行词汇,很多人声称自己设计的API是"RESTful API"。但据我观察,大部分人对REST的真正含义知之甚少,很多人对REST的一些核心概念尚未真正理解,甚至存在误解。那么,到底什么是REST,什么样的API可以叫做”RESTful API“呢?本文试图回答这两个问题。
REST
来源
REST一词来源于Roy Thomas Fielding在2000年发表的博士论文,是"Representational State Transfer"的缩写,这个词组可以翻译为"表述性状态转移"。
概念
REST是一种为分布式超媒体系统设计的架构风格。万维网是REST架构风格的一个应用实例。
特点
- 客户-服务器
- 无状态
- 缓存
- 统一接口
- 分层系统
- 按需代码
架构约束
REST架构风格区别于其他基于网络的架构风格的核心特征是,它强调组件之间要有一个统一的接口。实现与它们所提供的服务是解耦的,这促进了独立的可进化性。为了获得统一的接口,需要有多个架构约束来指导组件的行为。
REST由四个接口架构约束来定义:
- 资源的识别
- 通过表述来操作资源
- 自描述的消息
- 超媒体作为应用状态的引擎。
组件
REST组件可根据它们在整个应用动作中的角色来进行分类,如下表所示:
| 组件 | 例子 |
|---|---|
| origin server | Apache httpd |
| gateway | Nginx |
| proxy | Netscape代理 |
| user agent | web浏览器 |
统一接口理解起来比较抽象,这里,我们拿origin server 和 user agent举例说明,前者可以理解为服务器,后者可以理解为客户端,基于HTTP的REST接口,客户端并不关心提供接口的服务端是采用何种语言编写,也不关心服务端的架构,因此在接口协议不变的情况下,客户端和服务器可以独立进化,因此达到解耦。
数据元素
REST的关键方面之一是架构的数据元素的性质和状态。REST聚焦于分享对于数据类型的理解,但是对于作为标准化接口暴露的操作的范围进行了限制。REST组件之间通过以一种数据格式移交资源的表述来进行通信。
REST的数据元素如下表所示:
| 数据元素 | 现代web实例 |
|---|---|
| 资源 | 一份文档或一张图片 |
| 资源标识符 | URL、URN |
| 表述 | HTML、JSON |
| 表述元数据 | 媒体类型 |
资源
任何能够被命名的信息都能够作为一个资源,任何一个URL都应该符合资源的定义。
表述
某个资源的当前状态或预期状态。实例:HTTP消息实体。
媒体类型
表述的数据格式被称为媒体类型。
超媒体
包含原生超链接元素的媒体类型,称为超媒体类型,简称超媒体。
这个定义比较抽象,你可以这样来理解:
超媒体是对类似于HTML链接、表单等的事物抽象出来的通用术语,服务器端可以通过这种技术来向客户端说明下一步的操作。
REST的灵魂HATEOAS(超媒体作为应用状态的引擎)该如何理解呢?
回想一下我们是如何浏览Web的,是不是大体上可以总结为要么填写表单要么访问各种链接?
我们所说的超媒体作为应用状态的引擎,类比的就是这种场景。
RESTful API
RESTful架构
如果一个架构满足REST约束,就称它为RESTful架构。
同理,如果一个Web API满足REST约束,就称它为RESTful API。
Web API落后于Web
现今大部分的API都有着一个很大的问题:一旦部署,它们将无法改变。
它们通常有如下几个特征:
- Web API经常有大量的阅读文档来告诉你API提供者是如何为不同的接口设计URL的。在REST的世界中,将有关构造URL的信息放到单独的阅读文档中违背了连通性和自描述信息的原则。
- 现今的Web API呈现资源的方式更像是一个巨型的选项菜单,而不是一张相互连通的网。这使得API使用者很难了解资源之间的相互影响。
- 要集成一个新的API不可避免地需要编写新的定制化软件,或者安装别人编写的一次性的代码库。
- 当Web API发生了变化以后,定制化的API客户端就不能正常使用了,并且需要维护者为此进行一些代码修复。
超媒体的好处
上文简单列举了现今大部分API存在的一些问题,这些问题通常也比较难以简单解决,尤其是最后两个问题,相当棘手。
如何解决?超媒体正是解决之道,利用超媒体,可以构建因时而变的服务。
当API发生变化之后,API提供者不再需要考虑是否要兼容旧的API版本,而是可以像Web开发者一样,在客户端无感知的情况下平滑升级API。因为基于超媒体约束,API提供者和消费者是解耦的,就好比针对接口编程和针对实现编程的差异。
因此,利用超媒体,可以创建可不断演进的超媒体应用。
超媒体很难
尽管超媒体是解决现今一些API痛点问题的关键,但大部分API提供者并没有使用它。其中的原因,除了难以理解之外,还有一个更重要的因素:语义挑战。
语义挑战
回想一下我们是如何上网的:给我们一个网址,不需要借助额外的说明文档,我们就可以愉快并且顺利地进行网上冲浪了,我们点击了一些页面的超链接或者提交了某个表单,都是对资源的操作,要么从一个资源跳到另一个资源,要么改变了资源的状态,我们访问网站的过程非常顺利,这要归功于一个运行速度很慢但是又非常昂贵的硬件:人。因为人类可以理解表单背后的语义信息,以及一个超链接即将跳转的去处,而这却是计算机的弱项。
RESTful API的目标是没有人类参与的前提下完成相应的工作。但是我们该如何编写程序让计算机来决定单击哪个链接呢?计算机可以解析HTML标签<a href="/messages">Get Started</a>。但是它并不理解"Get Started"这个词组。如果提供的自描述信息不能被软件理解,我们又何苦设计这种提供自描述信息的API呢?
RESTful API设计最大的挑战是:消除"理解文档的结构"和"理解文档的含义"之间的语义鸿沟。即:语义挑战。
目前,这方面的进展非常小,我们也不可能完全解决它。
如何达成REST
正是因为完全符合Fielding博士REST约束的架构很难实现,目前应用REST的成功案例也仅有一个万维网。
Richardson在考察了数百种web service设计案例之后,提出了一种成熟度模型,用来评估一个web service接近REST的程度。
Richardson成熟度模型

如图所示,该成熟度模型用4个层级来评估一个web service满足REST约束的程度,分别是:
- 第0级:基于HTTP协议
- 第1级:抽象资源
- 第2级:利用HTTP动词
- 第3级:超媒体控制
第0级:基于HTTP协议

这一级的web service仅仅将HTTP当做一种传输协议来使用,通常只有一个URI,只使用POST或GET方法,
使用方式上更像远程过程调用。
第1级:有清晰的资源抽象

这一级的web service不再使用单一URI,而是使用很多不一样的URI来映射不同的接口,初步具有了资源的概念,但一个URI通常依旧只使用GET或POST其中一种方法。
第2级:合理使用HTTP动词

这一级的web service除了严格按照ROA(Resource-Oriented Architecture,面向资源架构)来设计URI,更加充分地利用了HTTP动词的语义,每一个URI可支持多个不同的HTTP方法,用来映射对资源的不同操作。
第3级:能够完美支持HATEOAS

这一级的web service在第2级的基础上应用了超媒体类型,在资源表述中加入了自描述的消息和超媒体控件,实现了超文本驱动,即HATEOAS(超媒体作为应用状态的引擎)。
结束语
正是因为设计出完全满足REST约束的API很难,目前大部分的Web API还停留在第1级或第2级的阶段,但是REST是值得我们追求的,因为它是构建可不断演进的API的解决方案。
目前来说,只要我们大体上能朝着第3级成熟度去努力,先部分拥抱REST的一些约束,使我们的API能够达到第2级,快速解决手头的一些问题,就可以认为这是一个RESTful API了。