REST 介绍
如果要说什么是 REST 的话,那最好先从 Web(万维网)说起。
什么是 Web 呢?读者可以查看维基百科的词条 ( http://zh.wikipedia.org/zh-cn/Web ),具体的我就不多说了。总之,Web 是我们在互联网上最常用的服务,甚至在某些人的心中,互联网就是 Web。当然,Web 只是互联网的一部分而已,只是大家用的最多而已,我们访问的所有网站都是基于 Web。
那么,Web 和 REST 之间究竟有什么关系呢?我们接下来将聊聊组成 Web 的几大基础技术, URI (统一资源标识符,用来标识资源)、 HTTP (超文本传输 / 转移协议,用来操作资源)、 Hypertext (超文本,用来描述资源的内容与状态,我们可以用 HTML、XML、JSON 或者自定义格式的文本来描述任何一个资源)。
那我们再来看看什么是 REST 呢?其实 REST 并不是一种新兴的技术语言,也不是什么新的技术框架。准确来说说 REST 只是一种概念、风格或者约束,是回归 HTTP 本身的建议。
REST 是由 Roy Thomas Fieding 在他的博士论文《Architectural Styles and the Design of Network-based Software Architectures》(《架构风格与基于网络的软件架构设计》) 中提出的一种架构思想。Roy Fielding 是 Apache 基金会的合作创作者,同时也是 HTTP、URI 等 Web 基础协议的主要设计者。从 Roy Fielding 的背景,我想大家就应该能了解到 REST 与 Web 之间的关系了吧。的确,在 REST 中我们关注技术实际上也只是 URI、HTTP、Hypertext 而已。
Roy 在他的论文中提出了一个 RESTful 应用应该具备的几点约束。
- 每个资源都应该有一个唯一的标识
- 使用标准的方法来更改资源的状态
- Request 和 Response 的自描述
- 资源多重表述
- 无状态的服务
Roy 认为,只有具备了上面的约束的应用才能算是 REST 应用,其实现在许多所谓的 REST 应用或服务,其实并不能算是真正的 REST 应用。
我发现,目前很多所谓的 REST 应用,其实只是 RPC 而已。出现这样的情况很正常,因为 RPC 更符合一般程序员的思维。可是,REST 和 RPC 之间还是有很大的差异的,下面我们说一说 REST 和 RPC 之间的区别。
- REST 强调资源有唯一的 URI;而 RPC 更加强大过程(动词),由统一的接口来调用它们。
- REST 回归 HTTP 最初的设计;RPC 仅仅只是把 HTTP 作为传输协议来使用。
- REST 是由超文本驱动的;RPC 是由方法驱动的。
- REST 强调 HTTP 通信的语义可见性,通过消息头和标准的 HTTP 方法来体现;RPC 把语义封装在 HTTP 消息体中。
REST 的应用场景
通过上面的介绍,大家应该对 REST 有一些最基本的了解,由于 REST 应用的这些约束,我们可以很轻易地了解和使用 REST 的服务(只要你了解 HTTP)。
其实,我们经常容易犯的一个错误是:当我们了解了一个新的技术,就会用这个技术来解决所有的问题。有一句谚语是这么来说的:“在锤子的眼里,所有的东西都是钉子”,其实 REST 也只是我们工具箱里面的其中的一个工具而已,希望不要把它当做我们唯一的工具。下面,我们就来聊聊适合使用 REST 的应用场景和不适合使用 REST 的应用场景。
在我看来,REST 最适合的应用场景是需要对外暴露服务的情况。此时,我们可以充分利用 REST 的自描述、无状态、唯一标识等特性来提供清晰、友好的 API;此外,目前 Jesery、RESTEasy 等 JAX-RS 框架也提供了 OAuth 的支持,基本上能够保证服务安全。
最不适合的应用场景是对性能要求高的系统内部的服务调用,在这种情况下使用 REST 的话,那么 REST 所有的特性都会变成拖累。这个时候,还是需要选择更底层的通信协议和方式会更好一些,比如 ICE。这样的错误,我曾经犯过,后来通过很长时间的努力才慢慢的将这个错误改过来。
规划 REST 服务
当我们要规划 REST 服务的时候,其中最关键的概念是“资源”。
资源是什么呢?广义上讲,任何事物只要有用,它就是资源。狭义的讲(在 Web 环境中),它是一个可以存放、连接在计算机上,可以通过比特流进行操控的实体。一个实体若要成为资源,它必须有一个 URI。在这里 URI 包含了两重含义:1) 它是该资源的名称 2) 它是该资源的地址。
在规划 URI 的时候,有几点希望大家能够注意一下:
- 一个 URI 标识一个资源,但是一个资源可以被多个 URI 标识。
- 资源也是有层次的,这个层次应该在 URI 上充分的体现出来。
- 在规划 URI 的时候,需要定义一些团队内部确认的关键字或符号,这些关键字或符号是有特殊意义的,不能随便使用。
- 需要有一个 URI 定义的文档,以备以后的查询和维护。
- 可以使用 URI Template 来描述 URI 的定义。如何使用 URI Template 请看看这篇文章。
当我们定义好资源之后,接下来要做的事情就是定义操作资源的方法以及资源的表述格式了。
使用 HTTP 提供的基本方法来对资源进行操作,一般的操作定义如下:POST(创建资源)、GET(获取资源)、PUT(修改资源)、DELETE(删除)。它们正好对应了 CRUD。
对资源的表述,一般的选择会是 XML,但是我更加推荐使用 JSON 来表述资源。在网络中的传输量小,而且也便于 JavaScript 解析,同时使用其他语言解析也是非常方便的事情。不过,最关键的还是占用更少的资源,让同样的资源能够服务于更多的人。
下面的这张图就很好地说明了 REST 中最重要的围绕资源的三角关系。
在 InfoQ 上有一篇很好的文章来介绍如何规划 REST 服务《如何获取(GET)一杯咖啡——星巴克REST 案例分析》,估计大家看完这篇文章,应该对如何规划REST 服务会有更深的认识。
选择一个快速方便的REST 框架
目前,REST 的框架非常多,推荐大家使用Jersey 和RESTEasy 来创建自己的REST 服务。
这两个框架都出自名门,Jersey 是由SUN 提供的JAX-RS 实现参考,对JAX-RS 支持的最为充分和快速,基本上所有的JAX-RS 的新特性都会在Jersey 里第一个体现出来,而且提供了相当齐备的例子让你学习。RESTEasy 则是JBoss 开源的项目,它同样有很多优点,文档也比Jersey 更好一些,但是它和JBoss 应用服务器绑定的比较紧密,这点我个人不太喜欢,如果你是熟悉JBoss 应用服务器的人,那就选择RESTEasy,它给人的感觉是更加成熟一些,不像Jersey 那样频繁地加入新特性。说到底,还是需要根据个人自己的喜好来选择。
如何使用Jersey 来快速创建REST 应用?参见通过Jersey 快速构建REST 应用;如何使用RESTEasy 快速创建REST 应用?参见使用RESTEasy 快速创建REST 应用。
发布REST 服务需要注意的地方
之前,我提到了使用REST 的最佳的场景是对外提供公开的服务,也就是所谓的OpenAPI。一旦开放了API,我们就很难控制这些API 的使用及其调整了,如果在开放这些API 之前考虑的不周到的话,那么后期的维护就会是一个非常麻烦的事情了。所以,当我们决定要开放API 的时候,我们一定要注意一些事情,下面的这些算是我的经验总结。
对外暴露API 时,需要注意版本规划,以便以后API 的升级和维护。在开始开放API 的之前,API 的版本规划是一件很容易被忽视的工作。但是一旦你的API 开放之后,你就会发现,没有对开放的API 进行版本规划,是一件非常愚蠢的事情。当使用你的API 的人越来越多,当你的开放的API 越来越多,一旦某个API 要升级,输入和输出发生变化的时候,你更不知道该通知谁来升级,解决问题的时候同样非常麻烦。
另外,由于对外暴露API 之后,你很难控制API 被调用的次数和意图,所以需要在一些关键的API 被调用的次数和频率上进行控制,以免受到恶意攻击。但是,访问次数和频率应该控制在怎样的程度,就要看你的API 的重要程度以及负载能力了,每个系统都会有自己的评判,只要你掌握好了这个尺度,应该都不会有问题的。
另外,由于当前浏览器的限制,只能使用HTTP 的GET 和POST 方法,如果通过AJAX 直接调用REST 服务,当你的服务中需要使用HTTP 的PUT 或者DELETE 方法调用服务时,最好应考虑使用重载POST 的方式,将需要使用PUT 和DELETE 方法调用的服务通过POST 方法调用。
关于作者
邓涛(Tony Deng),目前从事移动互联网领域,关注高性能、高并发的互联网架构。
感谢马国耀对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。
评论