InfoQ

InfoQ

文章

我的书签

登录注册 以永久保存书签。

该内容已经被标记书签!

标记书签错误,请重试!

REST服务开发实战

作者 邓涛 发布于 2011年2月22日

领域
企业架构,
架构 & 设计
主题
HTTP ,
SOA ,
W3C ,
REST ,
架构 ,
企业架构 ,
Sun Microsystems ,
规范 ,
《架构师》月刊

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中文站用户讨论组中与我们的编辑和其他读者朋友交流。

Good 发表人 Wong Kevin 发表于
Re: Good 发表人 邓 涛 发表于
一般情况下,put是创建,post是修改。 发表人 1107 ivy 发表于
Re: 一般情况下,put是创建,post是修改。 发表人 邓 涛 发表于
Re: 一般情况下,put是创建,post是修改。 发表人 1107 ivy 发表于
Re: 一般情况下,put是创建,post是修改。 发表人 hower eisen 发表于
Re: 一般情况下,put是创建,post是修改。 发表人 邓 涛 发表于
Re: 一般情况下,put是创建,post是修改。 发表人 qi liu 发表于
Re: 一般情况下,put是创建,post是修改。 发表人 Jin Crane 发表于
Re: 一般情况下,put是创建,post是修改。 发表人 tony cui 发表于
Re: 一般情况下,put是创建,post是修改。 发表人 haoxiang zhang 发表于
Re: 一般情况下,put是创建,post是修改。 发表人 du hongming 发表于
我现在用springmvc来实现rest服务,提供对外openapi,遇到问题如下 发表人 cao yunfei 发表于
Re: 我现在用springmvc来实现rest服务,提供对外openapi,遇到问题如下 发表人 邓 涛 发表于
  1. 返回顶部

    Good

    发表人 Wong Kevin

    写的不错,可以参照news.csdn.net/a/20101210/283438.html 这个演讲,技术专家李锟也有ppt可下载

  2. 返回顶部

    一般情况下,put是创建,post是修改。

    发表人 1107 ivy

    一般情况下,put是创建,post是修改。

  3. 返回顶部

    Re: Good

    发表人 邓 涛

    谢谢。
    锟哥为国内的REST推广作出很大的贡献,一直是我异常崇拜的偶像,等着拜读锟哥的最新翻译大作《REST in Practice》。

  4. 返回顶部

    Re: 一般情况下,put是创建,post是修改。

    发表人 邓 涛

    其实,这个也没有硬性的规定。不同的框架有不同的解释,在我的印象中Jersey和RESTeasy推荐POST是创建、PUT是修改;而Restlet推荐的是PUT是创建,POST是修改

  5. 返回顶部

    Re: 一般情况下,put是创建,post是修改。

    发表人 1107 ivy

    可能是的,我们只在用Restlet,哈哈,看来是我孤陋寡闻了啊,哈哈

  6. 返回顶部

    Re: 一般情况下,put是创建,post是修改。

    发表人 haoxiang zhang

    哥们,你说反了吧!REST里, POST是创建, PUT是修改。

  7. 返回顶部

    Re: 一般情况下,put是创建,post是修改。

    发表人 hower eisen

    区别的关键点是,操作是否等幂的.PUT操作多次都等幂的,而POST则不行。所以,PUT用于创建而POST用于更新。

  8. 返回顶部

    Re: 一般情况下,put是创建,post是修改。

    发表人 邓 涛

    区别的关键点是,操作是否等幂的.PUT操作多次都等幂的,而POST则不行。所以,PUT用于创建而POST用于更新。

    正因为PUT的幂等性,所以PUT更适合做更新,不明白的可以仔细想想。
    另外,实际的业务中,很少有更新是能够做到真正的幂等,大部分的更新都会触发其他的事件。

  9. 返回顶部

    Re: 一般情况下,put是创建,post是修改。

    发表人 qi liu

    按照幂等的概念,只要同一组数据运算后返回同一个结果就是幂等。那用什么post,put,过程如何都无所谓。关键是返回同一个结果。

  10. 返回顶部

    Re: 一般情况下,put是创建,post是修改。

    发表人 du hongming

    put 修改 post 创建 delete 删除 get 查看

  11. 返回顶部

    Re: 一般情况下,put是创建,post是修改。

    发表人 Jin Crane

    按照幂等的概念,只要同一组数据运算后返回同一个结果就是幂等。那用什么post,put,过程如何都无所谓。关键是返回同一个结果。

    幂等不是这个意思。 在这里可以理解为多次操作的结果跟一次操作是一样的。 比如abs(a)=abs(abs(a)) 貌似只有post不是幂等操作,但http协议对这个也不是强制的,自己让它幂等应该也可以

  12. 返回顶部

    我现在用springmvc来实现rest服务,提供对外openapi,遇到问题如下

    发表人 cao yunfei

    (1)、刚开始看springmvc的时候,觉得挺符合rest的,可以定义url如下“user/{userId}”,对应某个用户,现在发现这样写,也不能当作给用户提供的api啊,所以还得改成user/getuserInfo,呵呵,但是这样,好像又跟rest不符合了,怎么办才好呢

  13. 返回顶部

    Re: 一般情况下,put是创建,post是修改。

    发表人 tony cui

    jersey rest 怎么加入安全认证

  14. 返回顶部

    Re: 我现在用springmvc来实现rest服务,提供对外openapi,遇到问题如下

    发表人 邓 涛

    用户信息相关的API如果用REST的方式来表示的话可以是这样。

    获得用户信息:
    GET api.xxx.com/user/{userid}/info

    更新用户信息:
    PUT api.xxx.com/user/{userid}/info