正确理解RESTful API

Posted by Forgus on 2019-05-12

“你总提及的那个词,它的含义与你想表达的意思并不一样。”

前言

眼下,在技术圈,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成熟度模型

img

如图所示,该成熟度模型用4个层级来评估一个web service满足REST约束的程度,分别是:

  • 第0级:基于HTTP协议
  • 第1级:抽象资源
  • 第2级:利用HTTP动词
  • 第3级:超媒体控制

第0级:基于HTTP协议

level_zero

这一级的web service仅仅将HTTP当做一种传输协议来使用,通常只有一个URI,只使用POST或GET方法,

使用方式上更像远程过程调用。

第1级:有清晰的资源抽象

img

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

第2级:合理使用HTTP动词

img

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

第3级:能够完美支持HATEOAS

img

这一级的web service在第2级的基础上应用了超媒体类型,在资源表述中加入了自描述的消息和超媒体控件,实现了超文本驱动,即HATEOAS(超媒体作为应用状态的引擎)。

结束语

正是因为设计出完全满足REST约束的API很难,目前大部分的Web API还停留在第1级或第2级的阶段,但是REST是值得我们追求的,因为它是构建可不断演进的API的解决方案。
目前来说,只要我们大体上能朝着第3级成熟度去努力,先部分拥抱REST的一些约束,使我们的API能够达到第2级,快速解决手头的一些问题,就可以认为这是一个RESTful API了。