Restlet框架构建RESTful Web服务
导语
REST
是一种思维方式,而非协议或标准。它是设计基于命名资源而非消息的松耦合应用程序 — 通常指面向 Web 的应用程序 — 的一种风格。
构建 RESTful
应用程序的最困难的部分在于确定要公开哪些资源。解决了这个问题之后,再使用开源 Restlet
框架构建 RESTful Web 服务就是小菜一碟了
何为REST
REST
是设计基于命名资源 — 例如,以 Uniform Resource Locators(URL)、Uniform Resource Identifiers(URI)和 Uniform Resource Names(URN)的形式 — 而非消息的松耦合 Web 应用程序的一种风格。
REST 巧妙地借助已经验证过的成功的 Web 基础设施 — HTTP
。换句话说,REST 利用了 HTTP 协议的某些方面,例如 GET 和 POST 请求。这些请求可以很好地映射到标准业务应用程序需求,诸如创建、读取、更新和删除(CRUD)
CRUD | HTTP |
---|---|
创建 | POST |
读取 | GET |
更新 | PUT |
删除 | DELETE |
REST优点
构建 RESTful 系统并不难,且这样的系统具有高度的可伸缩性,同时与底层数据松散耦合;这样的系统还可以很好地利用缓存。
Web 上所有的东西(页面、图像等)本质上都是资源。 而 REST 正是基于命名资源而非消息的,这就限制了底层技术的曝光,从而给应用程序设计中的松耦合提供了便利条件。例如,下面的 URL 在不暗示任何底层技术的情况下,公开了资源:http://thediscoblog.com/2008/03/20/unambiguously-analyzing-metrics/。
该 URL 表示一个资源 — 一篇名为 “Unambiguously analyzing metrics” 的文章。请求该资源就会调用 HTTP GET 命令。注意该 URL 是基于名词的。基于动词的版本 http://thediscoblog.com/2008/03/20/getArticle?name=unambiguously-analyzing-metrics会违反 REST 原则,因为它以 getArticle 的形式嵌套了一条消息。
REST 的魅力在于任何东西都可以成为资源,且表示方法也可以不同。在前面的例子中,资源为一个 HTML 文件,因此,其响应可能是 HTML 格式的。但是资源也可以是一个 XML 文档、序列化的对象或者 JSON 表示。其实,这些都无关紧要。重要的是资源被命名了,并且与它通信不会影响其状态。不影响状态是很重要的,因为无状态的交互有利于可伸缩性。
REST的价值在那里
引用达芬奇的一句名言 “简洁就是终极复杂”。万维网的实现非常简单,并且无可置否地获得了成功。REST 正是利用了 Web 的简单性,并因此造就了高度可伸缩的、松散耦合的系统,而且事实证明,这样的系统很容易构建。
正如您所看到的,构建 RESTful 应用程序最难的部分在于确定要公开的资源。解决了这个问题之后,再使用开源 Restlet 框架构建 RESTful Web 服务就是小菜一碟了。
构建一个RESTful API
设想这样一个在线应用程序,它管理赛跑比赛,参赛人员要跑完不同的路程。应用程序管理赛跑以及与其相关的参赛人员。它会报告某个选手的时间(跑完全程所用的时间)和排名(参赛人员以第几名跑完全程)。赛事筹办公司 Acme Racing 要求您构建一个 RESTful Web 服务,主办方可以用它来为特定比赛安排新的赛事和选手,并且可以为某次特定比赛提供官方记录。 Acme Racing 已经有了一个遗留的胖客户机应用程序,它支持类似的请求,并利用了一个简单的数据库和一个域模型。因此,剩下的工作就只有公开这个功能了。记住 REST 的魅力就在于它与底层应用程序的隐式松散耦合。因此,您目前的工作并非是去操心数据模型或与其相关联的技术 — 而是去构造一个支持公司需求的 RESTful API。
Acme Races 希望主办方能够:
- 查看现有比赛细节
- 创建新的比赛
- 更新现有比赛
- 删除比赛
一个支持这些请求的 RESTful URI 应为 http://racing.acme.com/race。注意,在这种情况下,比赛是客户机要使用的资源。 用 HTTP
GET
来调用 URI 会返回一个比赛列表(这时先不要考虑响应的格式)。 要添加新比赛,要用包含适当信息(例如,一个包含诸如名称、日期和距离等信息的 XML 文档)的 HTTPPOST
来调用同一 URI。 要更新和删除现有比赛,则需要对特定比赛的实例进行操作。因此,可以给单个比赛赋予一个 URI:http://racing.acme.com/race/race_id。在这种情况下,race_id 表示任一比赛标识符的一个占位符(诸如 1 或者 600 米)。因此,查看一个现有比赛实例就是针对该 URI 执行一个 HTTPGET
请求;更新或者删除一个比赛分别为一个PUT
或者DELETE
请求。
Acme Racing 可能还希望公开有关某次比赛的参赛人员的数据。他们希望他们的服务支持:
- 获得有关特定比赛的全部参赛人员的数据。该数据还要包含已结束的比赛的赛跑时间和排名。
- 为特定比赛创建一个或多个参赛人员。
- 更新特定比赛的某一参赛人员的信息(如年龄)。
- 删除特定比赛的某一参赛人员。
和比赛一样,将 RESTful URI 应用于与比赛相关联的参赛人员同样是一个逻辑行为。例如,查看特定比赛的全部参赛人员可以通过对 http://racing.acme.com/race/race_id/runner 的 GET 请求来实现。 要获得一个比赛的某个参赛人员的个人信息,可以编址为 http://racing.acme.com/race/race_id/runner/runner_id。 向一个比赛添加参赛人员就是一个对 http://racing.acme.com/race/race_id/runner 的 POST 请求。更新或删除特定参赛人员则分别是对 http://racing.acme.com/race/race_id/runner/runner_id 的 PUT 和 DELETE 请求。
因此,这些 URI(每一个 URI 都支持四个标准 HTTP 请求的其中一些或者全部)就满足了 Acme Racing 的需求:
- /race
- /race/race_id
- /race/race_id/runner
- /race/race_id/runner/runner_id
一个 URI 可以映射到不止一个 HTTP 动词(例如,将一个 HTTP GET 应用到 /race 将返回数据;使用 POST 和适当的数据在服务器上创建数据)。不过,有些 HTTP 命令不能实现。例如,/race 可能不支持 DELETE 命令(Acme Racing 不会删除所有的比赛);而/race/race_id 可能支持 DELETE 命令,因为移除一个比赛的某个特定实例是一个业务需求。
通信的媒介XML
接下来构造一系列的 XML 文档来表示 RESTful 比赛 Web 服务将会支持的资源。
- GET 到 /races/1 的请求的响应:
注意这个URI请求就会返回id=1的race的信息。
- 创建一个新比赛的POST请求:
注意这里就没有id和uri了。
- GET到 /race/9/runner 的请求的响应:
注意这个URI请求就会返回id=9的race的所有runner列表。
- GET到 /race/1/runner/1 的请求的响应:
注意这个URI请求就会返回id=1的race的id=1的runner信息。
Restlet框架
Restlet
应用程序与 servlet
应用程序有一个相似点,就是它们都处在容器中,但实际上它们在两个方面是截然不同的。
- Restlet 不使用 HTTP 的直接概念或其状态显示,如 cookies 或者 session。
- Restlet 框架极其轻便。只需更新
web.xml
文件,并部署一个WAR
就可以了
基类
基本上,一个用 Restlet 框架构建的 RESTful 应用程序的大部分都需要使用两个基类: Application
和 Resource
。
- Application 实例将 URI 映射到 Resource 实例。
- Resource 实例处理基本的 CRUD 命令,当然,这些命令都要映射到 GET、POST、PUT 和 DELETE。
Application子类
通过 Router
将URI 映射到 Resource
子类。
Resource子类
Resource
基类定义了get/post/put/delete的模板方法,子类需要覆盖实现。
具体来说,在 GET 的情况下构建XML,在 POST、PUT 或者 DELETE 的情况下操作XML。
Groovy生成和操作XML
这里将使用Groovy来生成 XML 并完成操作 XML 文档这个沉闷的工作。
比如对于这个XML文件:
创建上面的XML:
获取
获得描述
Groovy代码
直接用Groovy写class即可,然后在其他Java类中可以直接import进这个class,就和普通Java类是一样的用法。 因为本质上Groovy的源代码.groovy是可以被编译为字节码再执行的,就是说本质上和Java源代码是一致的。
数据层
这层就是WEB传统的由spring和hibernate管理的数据持久层。和Restlet松耦合。
比如Race的API:
部署
需要将Restlet部署进servlet容器中:
代码
这篇文章是在IBM看到的,原文翻译的不好,我这里又整理了的。
最后附上代码下载
-EOF-