日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

构建 RESTful Web 服务

發(fā)布時(shí)間:2025/7/25 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 构建 RESTful Web 服务 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

from: https://www.ibm.com/developerworks/cn/education/java/j-rest/j-rest.html


開始之前

關(guān)于本教程

REST 是一種思維方式,而非協(xié)議或標(biāo)準(zhǔn)。它是設(shè)計(jì)基于命名資源而非消息的松耦合應(yīng)用程序 — 通常指面向 Web 的應(yīng)用程序 — 的一種風(fēng)格。在本教程中,您將了解到何為 REST 以及如何使用 Restlet(一個(gè)面向 Java? 應(yīng)用程序的輕量級(jí)框架)構(gòu)建 RESTful 應(yīng)用程序。

目標(biāo)

本教程從 REST 的基本概念開始逐步指導(dǎo)您使用 Restlet 構(gòu)建應(yīng)用程序。您將學(xué)習(xí)如何:

  • 定義 RESTful Web 服務(wù)
  • 用 Restlet 框架實(shí)現(xiàn)它們
  • 用 JUnit 測(cè)驗(yàn)框架驗(yàn)證它們

學(xué)完本教程后,您就會(huì)領(lǐng)會(huì)到使用 RESTful 原則進(jìn)行設(shè)計(jì)的益處,了解到 Restlet 框架是如何輕松地實(shí)現(xiàn)它們的。

先決條件

為了更好地學(xué)習(xí)本教程,您需要熟悉 Java 語(yǔ)法以及 Java 平臺(tái)中面向?qū)ο箝_發(fā)的基本概念。還要熟悉 Web 應(yīng)用程序。熟悉 Groovy、Unit、DbUnit 和 XMLUnit 對(duì)此也很有幫助。

系統(tǒng)需求

要跟隨并試用本教程中的代碼,您需要如下列出的任意一種可行的安裝:

  • Sun's JDK 1.5.0_09(或更高版本)
  • IBM Developer Kit for Java technology 1.5.0 SR3
  • Apache Ant 1.7 或者更高級(jí)版本

本文的源代碼有兩種版本(參見 下載)。其中一個(gè)版本包含了全部代碼以及所需依賴項(xiàng)(Restlet 框架 JUnit、XMLUnit 和 DbUnit)。使用低帶寬連接的讀者可以從它們各自的站點(diǎn)分別下載 Restlet 框架、JUnit、XMLUnit 和 DbUnit(參見 參考資料)并使用不包含依賴項(xiàng)的 下載 包。

要學(xué)習(xí)本教程,推薦的系統(tǒng)配置為:

  • 支持 Sun JDK 1.5.0_09(或更新版本)或者 IBM JDK 1.5.0 SR3 的系統(tǒng),主內(nèi)存至少為 500MB
  • 有至少 20MB 的磁盤空間安裝軟件組件和涉及到的示例

本教程中的指令和示例基于 Microsoft? Windows? 操作系統(tǒng)。教程中所用到的所有工具在 Linux? 和 UNIX? 系統(tǒng)上都可以運(yùn)行。

何為 REST?

REST 是設(shè)計(jì)基于命名資源 — 例如,以 Uniform Resource Locators(URL)、Uniform Resource Identifiers(URI)和 Uniform Resource Names(URN)的形式 — 而非消息的松耦合 Web 應(yīng)用程序的一種風(fēng)格。REST 巧妙地借助已經(jīng)驗(yàn)證過(guò)的成功的 Web 基礎(chǔ)設(shè)施 — HTTP。換句話說(shuō),REST 利用了 HTTP 協(xié)議的某些方面,例如 GET 和 POST 請(qǐng)求。這些請(qǐng)求可以很好地映射到標(biāo)準(zhǔn)業(yè)務(wù)應(yīng)用程序需求,諸如創(chuàng)建、讀取、更新和刪除(CRUD),如表 1 所示:

表 1. CRUD/HTTP 映射
應(yīng)用程序任務(wù) HTTP 命令創(chuàng)建讀取更新刪除

請(qǐng)求就像是動(dòng)詞,而資源就像是名詞,把兩者相關(guān)聯(lián)就形成了對(duì)行為的邏輯表達(dá) — 例如, GET 這個(gè)文件,DELETE 那條記錄。

真正的 REST 之父 Roy Fielding 在他的博士畢業(yè)論文中陳述到:REST “強(qiáng)調(diào)組件交互的可伸縮性、界面的普遍性、獨(dú)立部署組件以及使用中間組件來(lái)減少交互延遲,增強(qiáng)安全性并封裝遺留系統(tǒng)”(參見 參考資料)。構(gòu)建 RESTful 系統(tǒng)并不難,且這樣的系統(tǒng)具有高度的可伸縮性,同時(shí)與底層數(shù)據(jù)松散耦合;這樣的系統(tǒng)還可以很好地利用緩存。

Web 上所有的東西(頁(yè)面、圖像等)本質(zhì)上都是資源。而 REST 正是基于命名資源而非消息的,這就限制了底層技術(shù)的曝光,從而給應(yīng)用程序設(shè)計(jì)中的松耦合提供了便利條件。例如,下面的 URL 在不暗示任何底層技術(shù)的情況下,公開了資源:http://thediscoblog.com/2008/03/20/unambiguously-analyzing-metrics/。

該 URL 表示一個(gè)資源 — 一篇名為 “Unambiguously analyzing metrics” 的文章。請(qǐng)求該資源就會(huì)調(diào)用 HTTP GET 命令。注意該 URL 是基于名詞的。基于動(dòng)詞的版本(大概類似 http://thediscoblog.com/2008/03/20/getArticle?name=unambiguously-analyzing-metrics)會(huì)違反 REST 原則,因?yàn)樗?getArticle 的形式嵌套了一條消息。您也可以設(shè)想通過(guò) HTTP 的 POST 命令來(lái)發(fā)布一個(gè)新資源,(比如說(shuō),一篇諸如 http://thediscoblog.com/2008/03/22/rest-is-good-for-you/ 的文章)。你還可以設(shè)想用關(guān)聯(lián)的、基于動(dòng)詞的 API — 如 createArticle?name=rest-is-good-for-you and deleteArticle?name=rest-is-good-for-you — 這樣的調(diào)用來(lái)攔截 HTTP GET 命令,并最大限度地忽略已有的(并且是成功的)HTTP 基礎(chǔ)設(shè)施。換句話說(shuō),它們不是 RESTful 風(fēng)格。

REST 的魅力在于任何東西都可以成為資源,且表示方法也可以不同。在前面的例子中,資源為一個(gè) HTML 文件,因此,其響應(yīng)可能是 HTML 格式的。但是資源也可以是一個(gè) XML 文檔、序列化的對(duì)象或者 JSON 表示。其實(shí),這些都無(wú)關(guān)緊要。重要的是資源被命名了,并且與它通信不會(huì)影響其狀態(tài)。不影響狀態(tài)是很重要的,因?yàn)闊o(wú)狀態(tài)的交互有利于可伸縮性。

它的價(jià)值在那里?

引用達(dá)芬奇的一句名言 “簡(jiǎn)潔就是終極復(fù)雜”。萬(wàn)維網(wǎng)的實(shí)現(xiàn)非常簡(jiǎn)單,并且無(wú)可置否地獲得了成功。REST 正是利用了 Web 的簡(jiǎn)單性,并因此造就了高度可伸縮的、松散耦合的系統(tǒng),而且事實(shí)證明,這樣的系統(tǒng)很容易構(gòu)建。

正如您所看到的,構(gòu)建 RESTful 應(yīng)用程序最難的部分在于確定要公開的資源。解決了這個(gè)問(wèn)題之后,再使用開源 Restlet 框架構(gòu)建 RESTful Web 服務(wù)就是小菜一碟了。

起跑:構(gòu)建一個(gè) RESTful API

在本節(jié)中,您將為一個(gè) Web 服務(wù)構(gòu)建一個(gè) RESTful API,該服務(wù)利用了支持?jǐn)?shù)據(jù)庫(kù)的現(xiàn)有應(yīng)用程序的功能。

RESTful 比賽

設(shè)想這樣一個(gè)在線應(yīng)用程序,它管理賽跑比賽,參賽人員要跑完不同的路程(比如芝加哥馬拉松賽跑)。應(yīng)用程序管理賽跑(或者賽事)以及與其相關(guān)的參賽人員。它會(huì)報(bào)告某個(gè)選手的時(shí)間(跑完全程所用的時(shí)間)和排名(參賽人員以第幾名跑完全程)。賽事籌辦公司 Acme Racing 要求您構(gòu)建一個(gè) RESTful Web 服務(wù),主辦方可以用它來(lái)為特定比賽安排新的賽事和選手,并且可以為某次特定比賽提供官方記錄。

Acme Racing 已經(jīng)有了一個(gè)遺留的胖客戶機(jī)應(yīng)用程序,它支持類似的請(qǐng)求,并利用了一個(gè)簡(jiǎn)單的數(shù)據(jù)庫(kù)和一個(gè)域模型。因此,剩下的工作就只有公開這個(gè)功能了。記住 REST 的魅力就在于它與底層應(yīng)用程序的隱式松散耦合。因此,您目前的工作并非是去操心數(shù)據(jù)模型或與其相關(guān)聯(lián)的技術(shù) — 而是去構(gòu)造一個(gè)支持公司需求的 RESTful API。

比賽 URI

Acme Races 希望主辦方能夠:

  • 查看現(xiàn)有比賽細(xì)節(jié)
  • 創(chuàng)建新的比賽
  • 更新現(xiàn)有比賽
  • 刪除比賽

由于 REST 最終歸結(jié)為命名資源,API 就成為了一系列 URI 模式,并且與資源相關(guān)聯(lián)的行為要通過(guò)標(biāo)準(zhǔn) HTTP 命令調(diào)用。

正如您可以看到的,客戶的請(qǐng)求很好地映射到了 CRUD。并且如您在 表 1 中所了解到的一樣,REST 分別通過(guò) HTTP 的 POST、GET、PUT 以及 DELETE 請(qǐng)求來(lái)支持 CRUD。因此,一個(gè)支持這些請(qǐng)求的基 RESTful URI 應(yīng)為 http://racing.acme.com/race。注意,在這種情況下,比賽是客戶機(jī)要使用的資源。

用 HTTP GET 來(lái)調(diào)用 URI 會(huì)返回一個(gè)比賽列表(這時(shí)先不要考慮響應(yīng)的格式)。要添加新比賽,要用包含適當(dāng)信息(例如,一個(gè)包含諸如名稱、日期和距離等信息的 XML 文檔)的 HTTP POST 來(lái)調(diào)用同一 URI。

要更新和刪除現(xiàn)有比賽,則需要對(duì)特定比賽的實(shí)例進(jìn)行操作。因此,可以給單個(gè)比賽賦予一個(gè) URI:http://racing.acme.com/race/race_id。在這種情況下,race_id 表示任一比賽標(biāo)識(shí)符的一個(gè)占位符(諸如 1 或者 600 米)。因此,查看一個(gè)現(xiàn)有比賽實(shí)例就是針對(duì)該 URI 執(zhí)行一個(gè) HTTP GET 請(qǐng)求;更新或者刪除一個(gè)比賽分別為一個(gè) PUT 或者 DELETE 請(qǐng)求。

Acme Racing 可能還希望公開有關(guān)某次比賽的參賽人員的數(shù)據(jù)。他們希望他們的服務(wù)支持:

  • 獲得有關(guān)特定比賽的全部參賽人員的數(shù)據(jù)。該數(shù)據(jù)還要包含已結(jié)束的比賽的賽跑時(shí)間和排名。
  • 為特定比賽創(chuàng)建一個(gè)或多個(gè)參賽人員。
  • 更新特定比賽的某一參賽人員的信息(如年齡)。
  • 刪除特定比賽的某一參賽人員。

Acme 可能還希望該服務(wù)可以讓用戶查看特定比賽的某個(gè)參賽人員的個(gè)人數(shù)據(jù)。

和比賽一樣,將 RESTful URI 應(yīng)用于與比賽相關(guān)聯(lián)的參賽人員同樣是一個(gè)邏輯行為。例如,查看特定比賽的全部參賽人員可以通過(guò)對(duì) http://racing.acme.com/race/race_id/runner 的 GET 請(qǐng)求來(lái)實(shí)現(xiàn)。

要獲得一個(gè)比賽的某個(gè)參賽人員的個(gè)人信息,可以編址為 http://racing.acme.com/race/race_id/runner/runner_id。

和 race_id 一樣,runner_id 是 ID 的邏輯實(shí)現(xiàn)的一個(gè)占位符,這些 ID 可以是數(shù)字、姓名、字母數(shù)字組合等。

向一個(gè)比賽添加參賽人員就是一個(gè)對(duì) http://racing.acme.com/race/race_id/runner 的 POST 請(qǐng)求。更新或刪除特定參賽人員則分別是對(duì) http://racing.acme.com/race/race_id/runner/runner_id 的 PUT 和 DELETE 請(qǐng)求。

因此,這些 URI(每一個(gè) URI 都支持四個(gè)標(biāo)準(zhǔn) HTTP 請(qǐng)求的其中一些或者全部)就滿足了 Acme Racing 的需求:

  • /race
  • /race/race_id
  • /race/race_id/runner
  • /race/race_id/runner/runner_id

記住,一個(gè) URI 可以映射到不止一個(gè) HTTP 動(dòng)詞(例如,將一個(gè)HTTP GET 應(yīng)用到 /race 將返回?cái)?shù)據(jù);使用 POST 和適當(dāng)?shù)臄?shù)據(jù)在服務(wù)器上創(chuàng)建數(shù)據(jù))。因此,有些 HTTP 命令不能實(shí)現(xiàn)。例如,/race 可能不支持 DELETE 命令(Acme Racing 不會(huì)刪除所有的比賽);/race/race_id 可能支持 DELETE 命令,因?yàn)橐瞥粋€(gè)比賽的某個(gè)特定實(shí)例是一個(gè)業(yè)務(wù)需求。

格式化資源

在本小節(jié)中,您將構(gòu)造一系列的 XML 文檔來(lái)表示 RESTful 比賽 Web 服務(wù)將會(huì)支持的資源。

比賽 URI

前一小節(jié)中為 Acme Racing 構(gòu)建的 RESTful API 涉及了網(wǎng)絡(luò)端點(diǎn)或者 URI,但并未提及資源。正如我在前面提到的,就 REST 而言,資源的格式并不重要。例如,您可以來(lái)回傳送 XML 或者二進(jìn)制流。

在業(yè)務(wù)事務(wù)環(huán)境中,XML 是不折不扣的在機(jī)器之間通信的混合語(yǔ)言,所以構(gòu)造一系列受 RESTful 服務(wù)支持的 XML 文檔很有意義。競(jìng)賽的域相當(dāng)簡(jiǎn)單,而且您可以使用現(xiàn)有的數(shù)據(jù)模型,所以定義幾個(gè)表示比賽和參賽人員的 XML 文檔也很簡(jiǎn)單。

例如,可以用清單 1 中的 XML 定義一個(gè)比賽:

清單 1. 某次比賽的 XML 文檔
1 2 3 4 <race name="Mclean 1/2 Marathon" date="2008-05-12" distance="13.1" id="1"> ?<uri>/races/1</uri> ??<description/> </race>

注意一個(gè) <race> 有一個(gè) id,且清單 1 包含一個(gè)作為比賽定義的一部分的 id。這是 REST 與 Web 的一個(gè)關(guān)鍵的方面 — 資源是相關(guān)的,應(yīng)該鏈接在一起。因此,一個(gè) <race> 總是含有一個(gè)描述它的 RESTful 表示的 <uri> 元素。清單 1 中的 XML 是一個(gè) GET 到 /races/1 的請(qǐng)求的響應(yīng)。

要?jiǎng)?chuàng)建一個(gè)新比賽,您可以省略 id 方面(因?yàn)楣芾砦┮?ID 是由您正在構(gòu)建的應(yīng)用程序控制的)。這暗示您可以排除 <uri> 元素。因此,POST 請(qǐng)求應(yīng)該如清單 2 所示:

清單 2. 創(chuàng)建比賽的 XML
1 2 3 <race name="Limerick 2008 Half" date="2008-05-12" distance="13.4"> ?<description>erin go braugh and have a good time!</description> </race>

那參賽人員呢?參賽人員與比賽有關(guān),對(duì)么?所以 <race> 元素支持保存一到兩個(gè) <runner> 元素,如清單 3 所示:

清單 3. 與比賽相關(guān)的參賽人員
1 2 3 4 5 6 7 8 9 10 11 12 <race name="Limerick 200 Half" date="2008-05-12" distance="13.4" id="9"> ?<uri>races/9</uri> ?<description>erin go braugh and have a good time!</description> ?<runners> ??<runner first_name="Linda" last_name="Smith" age="25" id="21"> ???<uri>/races/9/runner/21</uri> ??</runner> ??<runner first_name="Andrew" last_name="Glover" age="22" id="20"> ???<uri>/races/9/runner/20</uri> ??</runner> ?</runners> </race>

例如,清單 3 中的 XML 文檔就是通過(guò) URI /race/race_id/runner 而返回的。API 還支持通過(guò) URI /race/race_id/runner/runner_id 對(duì)單一參賽人員執(zhí)行的 CRUD 操作。

因此,這些 CRUD 操作的 XML 如清單 4 所示:

清單 4. CRUD XML
1 2 3 4 5 6 7 8 <race name="Mclean 1/2 Marathon" date="2008-05-12" distance="13.1" id="1"> ?<uri>/races1</uri> ?<description /> ?<runner first_name="Andrew" last_name="Glover" age="32" id="1"> ??<uri>/races/1/runner/1</uri> ??<result time="100.04" place="45" /> ?</runner> </race>

注意,如果比賽已經(jīng)完成,XML 文檔中會(huì)包含參賽人員的比賽結(jié)果。記住,使用一個(gè) POST 請(qǐng)求就意味著創(chuàng)建一個(gè)參賽人員,因此,<runner> 元素的 id 屬性不會(huì)呈現(xiàn)。

Restlets

您已經(jīng)定義了一個(gè) RESTful API 了,它可以很好地映射到 CRUDing 比賽和參賽人員。并且您已經(jīng)定義了通信的格式:XML 文檔。在這一小節(jié)中,您將開始用一個(gè)仿效 servlet 的創(chuàng)新的框架將所有這些組合起來(lái)。

Restlet 框架

Restlet 應(yīng)用程序與 servlet 應(yīng)用程序有一個(gè)相似點(diǎn),就是它們都處在容器中,但實(shí)際上它們?cè)趦蓚€(gè)方面是截然不同的。第一,Restlet 不使用 HTTP 的直接概念或其狀態(tài)顯示,如 cookies 或者 session。第二,Restlet 框架極其輕便。正如您將要看到的,只用幾個(gè)核心 Restlet 基類擴(kuò)展出來(lái)的幾個(gè)類就能夠構(gòu)建一個(gè)功能完善的 RESTful 應(yīng)用程序。配置和部署利用現(xiàn)有的容器模型,所以您只需更新原來(lái)的 web.xml 文件,并部署一個(gè)標(biāo)準(zhǔn) Web 歸檔文件(WAR)就可以了。

基本上,一個(gè)用 Restlet 框架構(gòu)建的 RESTful 應(yīng)用程序的大部分都需要使用兩個(gè)基類: Application 和 Resource。邏輯上,Application 實(shí)例將 URI 映射到 Resource 實(shí)例。Resource 實(shí)例處理基本的 CRUD 命令,當(dāng)然,這些命令都要映射到 GET、POST、PUT 和 DELETE。

比賽應(yīng)用程序

通過(guò)擴(kuò)展 Restlet 框架的 Application 類來(lái)創(chuàng)建一個(gè)起跑點(diǎn)。在這個(gè)類中,定義響應(yīng) URI 的 Resource。該定義過(guò)程是用框架的 Router 類來(lái)完成的。例如,如果您有諸如 /order_id 這樣的 URI 的話,您需要指定哪一個(gè)對(duì)象可以處理這些請(qǐng)求。這個(gè)對(duì)象是框架的 Resource 類型的一個(gè)實(shí)例。可以通過(guò)將這些對(duì)象附加到 Router 實(shí)例來(lái)使它們同 URI 鏈接,如清單 5 所示:

清單 5. 創(chuàng)建 Router 實(shí)例并映射 URI
1 2 Router router = new Router(this.getContext()); router.attach("order/{order_id}", Order.class);

所以在這個(gè)例子中,URI order/order_id 被邏輯映射到 Order 類(它再擴(kuò)展 Resource)。

Acme Racing 擁有四個(gè)您已經(jīng)定義了的邏輯 RESTful URI — 處理比賽和參賽人員的各個(gè)方面的四種模式:

  • /race
  • /race/race_id
  • /race/race_id/runner
  • /race/race_id/runner/runner_id

每一個(gè) URI 的行為(比如,如果它使用 POST、DELETE、GET 等)在這點(diǎn)上并不重要。每一個(gè) Resource 的行為則是由 Resource 實(shí)例負(fù)責(zé);而 Application 實(shí)例用于通過(guò) Router 實(shí)例將這些 URI 映射到(尚未定義的)Resource ,如清單 6 所示:

清單 6. 將 Acme Racing 的 URI 映射到 Resource
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class RaceApplication extends Application{ ?public RaceApplication(Context context) { ??super(context); ?} ?public Restlet createRoot() { ??Router router = new Router(this.getContext()); ??router.attach("/race", RacesResource.class); ??router.attach("/race/{race_id}", RaceResource.class); ??router.attach("/race/{race_id}/runner", RaceRunnersResource.class); ??router.attach("/race/{race_id}/runner/{runner_id}", RaceRunnerResource.class); ??return router; ?} }

基類 Application 是一個(gè)抽象類。擴(kuò)展類必須實(shí)現(xiàn) createRoot() 方法。在這個(gè)方法中,可以創(chuàng)建一個(gè) Router 實(shí)例,并將 Resource 附加到 URI,像清單 6 中所示的那樣。

正如您所看到的,有四個(gè)不同的 Resource 類。我已經(jīng)將它們命名,使它們與期望的 URI 的高級(jí)行為相匹配。例如 /race URI 應(yīng)該處理多個(gè)比賽實(shí)例,因此 Resource 類型被命名為 RacesResource。一旦 URI(/race/race_id)中包含了 id,就暗示著正在操作一個(gè)單獨(dú)的比賽,因此,Resource 類型適合命名為 RaceResource。

比賽資源

定義了 Application 實(shí)例來(lái)處理四種不同的 URI 模式后,一定要實(shí)現(xiàn)四個(gè) Resource。

Restlet 框架中的 Resource 類型被認(rèn)為是 Restlet。它們是使用 Restlet 框架開發(fā)的 RESTful 的應(yīng)用程序的核心。與 Application 類型不同,基 Resource 類并非抽象類。它更像是一個(gè)可以按需要覆蓋其默認(rèn)行為的模版。

從較高的程度來(lái)看,Resource 有四個(gè)需要覆蓋的方法。它們映射到了基本的 HTTP 命令,這些命令是 REST — GET、POST、PUT 以及 DELETE— 的標(biāo)準(zhǔn),這并不是巧合。由于 Resource 類是非抽象類,所以框架需要為期望調(diào)用的行為實(shí)現(xiàn)一對(duì)方法。例如,如果想讓一個(gè)特定的資源來(lái)響應(yīng) DELETE 請(qǐng)求的話,首先要實(shí)現(xiàn) delete() 方法。第二,必須實(shí)現(xiàn) allowDelete() 方法,并且要讓這個(gè)方法返回 true(默認(rèn)為 false)。相應(yīng)的 PUT、POST 和 DELETE 默認(rèn)允許方法返回 false,而 allowGet() 方法默認(rèn)返回 true。這意味著對(duì)于只讀的 Resource,只需要覆蓋一個(gè)方法(而不是像在另外三種情況中覆蓋兩個(gè))。或者也可以調(diào)用 Resource 類中的 setModifcation(true),因此不需要覆蓋單個(gè) HTTP 動(dòng)詞 allow 方法。

例如,RacesResource 應(yīng)該用一個(gè)在系統(tǒng)中描述比賽的 XML 文檔來(lái)響應(yīng) GET 請(qǐng)求。用戶也可以通過(guò)這個(gè) Resource 類型創(chuàng)建新比賽。因此,RacesResource 類覆蓋了 Resource 基類中的至少三個(gè)方法:

  • getRepresentation()
  • allowPost()
  • post()

記住,Resource 實(shí)例默認(rèn)為只讀。因此,不需要覆蓋 allowGet() 方法。

生成 XML 文檔

在 格式化資源 小節(jié),我們決定利用 XML 作為客戶機(jī)和服務(wù)之間共享信息的數(shù)據(jù)機(jī)制。因此,Restlet 一定要操作 XML:在 GET 的情況下構(gòu)建它,在 POST、PUT 或者 DELETE 的情況下消費(fèi)它。在這一小節(jié)中,您將通過(guò)使用 Groovy 腳本語(yǔ)言來(lái)消除生成和操作 XML 文檔的痛苦(參見 參考資料)。

利用 Groovy

使用 XML 可不是個(gè)輕松的活兒。退一步說(shuō),這個(gè)工作很單調(diào)乏味,且容易發(fā)生錯(cuò)誤。幸運(yùn)的是,Groovy 讓使用 XML 簡(jiǎn)單了許多。

您將利用 Groovy 的力量來(lái)生成 XML 并完成操作 XML 文檔這個(gè)沉悶的工作。使用 Groovy 處理 XML 是再簡(jiǎn)單不過(guò)了。例如,解析 XML 文檔就很簡(jiǎn)單。以清單 7 中的 XML 文檔為例:

清單 7. 一個(gè)簡(jiǎn)單的待解析的 XML 文檔
1 2 3 4 5 6 <acme-races> ??<race name="Alaska 200 below" date="Thu Jan 01" distance="3.2" id="20"> ????<uri>/races/20</uri> ????<description>Enjoy the cold!</description> ??</race> </acme-races>

假設(shè)您想獲取 <race> 元素的 name 屬性的值,您只需向 Groovy 的 XMLSlurper 類傳入一個(gè) XML 文檔實(shí)例,調(diào)用 parse() 方法,然后定位到您想要的元素或者屬性就可以了,如清單 8 所示:

清單 8. 用 Groovy 解析 XML
1 2 def root = new XmlSlurper().parseText(raceXML) def name = root.race.@name.text()

如果您想獲得描述,調(diào)用 root.race.description.text() 就可以了。

創(chuàng)建 XML 也很簡(jiǎn)單。如果想創(chuàng)建清單 7 中的 XML 代碼片斷的話,只要?jiǎng)?chuàng)建一個(gè) Groovy 的 MarkupBuilder 類的實(shí)例,然后給它添加個(gè)節(jié)點(diǎn)就可以了,如清單 9 所示:

清單 9. 創(chuàng)建 XML 再簡(jiǎn)單不過(guò)了
1 2 3 4 5 6 7 8 9 def writer = new StringWriter() def builder = new MarkupBuilder(writer) builder."acme-races"() { ????race(name: "Alaska 200 below",? date: "Thu Jan 01", distance: "3.2", id: "20") { ????????uri("/races/20") ????????description("Enjoy the cold!") ????} } println writer.toString()

注意元素是如何通過(guò)附加名稱到 builder 實(shí)例而被添加到 XML 文檔的。我需要為 acme-races 加上引號(hào),因?yàn)樵?Groovy 字符串字母中不允許使用連字符,因此,使 acme-races 成為 String 就很好地解決了這個(gè)問(wèn)題。

元素可以有屬性。屬性名和屬性值是通過(guò)構(gòu)造 Groovy 映射創(chuàng)建的,同時(shí) Groovy 映射將兩者鏈接在一起(例如,name:"Alaska 200 below")。

數(shù)據(jù)層

本節(jié)描述了現(xiàn)有的域?qū)ο?#xff0c;它生成了 RESTful 服務(wù)將會(huì)重用的數(shù)據(jù)層。

域?qū)ο?/h3>

正如您從 起跑:構(gòu)建 RESTful API 一節(jié)了解到的,Acme Racing 投資于以前的項(xiàng)目的數(shù)據(jù)層,并且想將其重用于新的 Web 服務(wù)。當(dāng)然,這會(huì)讓您的工作更輕松。簡(jiǎn)單地說(shuō),該數(shù)據(jù)層由三個(gè)業(yè)務(wù)對(duì)象組成:Race、Runner 以及 Result。它們由 Spring 和 Hibernate 有效地管理著;然而,這些框架您是看不見的;您僅有一個(gè)運(yùn)行良好的 JAR 文件(換句話說(shuō),它可以讓您輕松創(chuàng)建新比賽,找到現(xiàn)有參賽人員等)。

業(yè)務(wù)對(duì)象支持一系列的尋找方法,它們使獲取比賽和參賽人員實(shí)例變得很輕松。對(duì)象可以分別通過(guò) save()、update() 和 remove() 方法被持久化、更新、以及從底層數(shù)據(jù)庫(kù)中移除。

例如,Race 對(duì)象支持一系列的尋找方法,并為良好地操作持久化數(shù)據(jù)提供便利條件。Race 對(duì)象的 API 很簡(jiǎn)單,如清單 10 所示:

清單 10. Race 的 API
1 2 3 4 5 6 Collection<Race> findAll(); Race findById(long id); Race findByName(String name); void create(Race race); void update(Race race); void remove(Race race);

一個(gè) Race 實(shí)例有很多的屬性,如清單 11 所示:

清單 11. Race 的屬性
1 2 3 4 5 6 7 private long id; private String name; private Date date; private double distance; private Set<Runner> participants; private Set<Result> results; private String description;

Race 的所有屬性都可以通過(guò) getter 和 setter 使用。而且,項(xiàng)集合(如 participants 和 results)支持添加單獨(dú)的項(xiàng)。因此,Race 對(duì)象有一個(gè) addParticipant() 方法,如清單 12 所示:

清單 12. Race 的 addParticipant() 方法
1 public void addParticipant(final Runner participant) ;

正如您所看到的,使用這個(gè)域模型是很簡(jiǎn)單的。

構(gòu)建與測(cè)試服務(wù)

知道如何使用 XML 并已經(jīng)有了可以用的數(shù)據(jù)層之后,是時(shí)候繼續(xù)使用 Restlet 構(gòu)建 RESTful 應(yīng)用程序并做一些測(cè)試準(zhǔn)備了。

比賽服務(wù)

回憶一下,Acme Racing 希望服務(wù)可以讓客戶機(jī)查看現(xiàn)有比賽,并創(chuàng)建新比賽。您已經(jīng)大致了解了支持 /race 這個(gè)行為的 RESTful URI。

通過(guò) RaceApplication 類中的 Router 類,可以將這個(gè) URI 鏈接到 RacesResource 類。您已經(jīng)知道了您必須實(shí)現(xiàn)三個(gè)方法:

  • getRepresentation()
  • allowPost()
  • post()

因此,創(chuàng)建一個(gè)叫做 RacesResource 的類,并確保它擴(kuò)展了 org.restlet.resource.Resource。另外,實(shí)現(xiàn)一個(gè)含三個(gè)參數(shù)的構(gòu)造函數(shù),如清單 13 所示:

清單 13. RacesResource 中含三個(gè)參數(shù)的構(gòu)造函數(shù)
1 2 3 4 5 public class RacesResource extends Resource { ?public RacesResource(Context context, Request request, Response response) { ??super(context, request, response); ?} }

一定要指導(dǎo) Restlet 如何正確通信資源表示。由于 XML 將會(huì)起到資源格式的作用,一定要通過(guò)添加 XML 變體類型來(lái)指導(dǎo) Restlet。Restlet 中的 Variant 表示 Resource 的格式。基類 Resource 含有一個(gè) getVariants() 方法,它使添加各種 Variant 類型變得容易。因此,將清單 14 中的內(nèi)容添加到您的構(gòu)造函數(shù):

清單 14. 將 XML 表示為一個(gè)變體
1 this.getVariants().add(new Variant(MediaType.TEXT_XML));

Restlet 框架支持很多種媒介類型,包括圖像和視頻。

處理 GET 請(qǐng)求

現(xiàn)在是時(shí)候?qū)崿F(xiàn)類的最簡(jiǎn)單行為了:處理 GET 請(qǐng)求。如清單 15 所示涵蓋 getRepresentation() 方法:

清單 15. 涵蓋 getRepresentation()
1 2 3 public Representation getRepresentation(Variant variant) { ?return null; }

可以看到,這個(gè)方法返回了一個(gè) Representation 類型,它有多種實(shí)現(xiàn)。其中一個(gè)實(shí)現(xiàn) — 適當(dāng)?shù)孛麨?StringRepresentation — 表示字符串,并會(huì)滿足您的需求。

正如您所知道的,您已經(jīng)有了一個(gè)遺留域模型,它可以支持使用數(shù)據(jù)庫(kù)。事實(shí)證明有人已經(jīng)寫出了實(shí)用程序類,叫做 RaceReporter,它可以將域?qū)ο筠D(zhuǎn)換為 XML 文件。這個(gè)類的 racesToXml() 方法使用了一個(gè) Race 實(shí)例集合,并返回了表示與清單 16 類似的 XML 文檔的 String:

清單 16. XML 響應(yīng)
1 2 3 4 5 6 7 8 9 10 11 12 <acme-races> ?<races> ??<race name="Leesburg 5K" date="2008-05-12" distance="3.1" id="5"> ??<uri>/races/5</uri> ??<description/> ?</race> ?<race name="Leesburg 10K" date="2008-07-30" distance="6.2" id="6"> ??<uri>/races/6</uri> ??<description/> ?</race> ?</races> </acme-races>

事實(shí)上,這個(gè) XML 文檔演示了在響應(yīng) GET 請(qǐng)求而調(diào)用 /race URI 時(shí) RESTful Web 服務(wù)將返回的內(nèi)容。

因此,我們的工作是鏈接底層數(shù)據(jù)存儲(chǔ)中所有比賽實(shí)例的檢索;其實(shí)在這一點(diǎn)上,您可能已經(jīng)編寫了一個(gè)測(cè)試了。

測(cè)試服務(wù)

使用 Restlet 框架,您能夠構(gòu)造客戶機(jī)實(shí)例,并讓它調(diào)用 RESTful Web 服務(wù)。此外,您可以利用 XMLUnit(參見 參考資源)來(lái)驗(yàn)證服務(wù)的輸出是一些已知的 XML 文檔。最后,同樣重要的是,您也可以使用 DbUnit(參見 參考資料)將底層數(shù)據(jù)庫(kù)設(shè)置為已知狀態(tài)(這樣就能總是獲得相同的 XML 文檔)。

可以使用 JUnit 4 創(chuàng)建兩個(gè) fixture,它可以適當(dāng)初始化 XMLUnit 和 DbUnit,如清單 17 所示:

清單 17. 設(shè)置 XMLUnit 和 DbUnit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Before public void setUpXMLUnit() { ?XMLUnit.setControlParser( ??"org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); ?XMLUnit.setTestParser( ??"org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); ?XMLUnit.setSAXParserFactory( ??"org.apache.xerces.jaxp.SAXParserFactoryImpl"); ?XMLUnit.setIgnoreWhitespace(true); } @Before public void setUpDbUnit() throws Exception { ?Class.forName("org.hsqldb.jdbcDriver"); ?IDatabaseConnection conn = ??new DatabaseConnection( ???getConnection("jdbc:hsqldb:hsql://127.0.0.1", "sa", "")); ?IDataSet data = new FlatXmlDataSet(new File("etc/database/race-db.xml")); ?try { ??DatabaseOperation.CLEAN_INSERT.execute(conn, data); ?} finally { ??conn.close(); ?} }

在 setUpDbUnit 方法中,數(shù)據(jù)庫(kù)的 XML 表示通過(guò) CLEAN_INSERT 命令被插入數(shù)據(jù)庫(kù)。這個(gè) XML 文件有效地插入了六個(gè)不同的比賽。因此,GET 的響應(yīng)將會(huì)是一個(gè)帶有六個(gè)比賽的 XML 文檔。

接下來(lái),可以創(chuàng)建一個(gè)測(cè)試用例,它可以對(duì) /race URI 調(diào)用 HTTP GET、獲取響應(yīng) XML 并使用 XMLUnit 的 Diff 類將它比作為控制 XML 文件,如清單 18 所示:

清單 18. 用 XMLUnit 驗(yàn)證 GET 響應(yīng)
1 2 3 4 5 6 7 8 9 10 11 @Test public void getRaces() throws Exception { ?Client client = new Client(Protocol.HTTP); ?Response response = ??client.get("http://localhost:8080/racerrest/race/"); ?Diff diff = new Diff(new FileReader( ??new File("./etc/control-xml/control-web-races.xml")), ???new StringReader(response.getEntity().getText())); ?assertTrue(diff.toString(), diff.identical()); }

control-web-races.xml 文件是預(yù)期的來(lái)自 Web 服務(wù)的 XML 響應(yīng)。它含有如清單 19 所示的數(shù)據(jù):

清單 19. 控制 XML 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <acme-races> ?<races> ??<race name="Mclean 1/2 Marathon" date="2008-05-12" distance="13.1" id="1"> ???<uri>http://localhost:8080/races/1</uri> ???<description/> ??</race> ??<race name="Reston 5K" date="2008-09-13" distance="3.1" id="2"> ???<uri>http://localhost:8080/races/2</uri> ???<description/> ??</race> ??<race name="Herndon 10K" date="2008-10-22" distance="6.2" id="3"> ???<uri>http://localhost:8080/races/3</uri> ???<description/> ??</race> ??<race name="Leesburg 1/2 Marathon" date="2008-01-02" distance="13.1" id="4"> ???<uri>http://localhost:8080/races/4</uri> ???<description/> ??</race> ??<race name="Leesburg 5K" date="2008-05-12" distance="3.1" id="5"> ???<uri>http://localhost:8080/races/5</uri> ???<description/> ??</race> ??<race name="Leesburg 10K" date="2008-07-30" distance="6.2" id="6"> ???<uri>http://localhost:8080/races/6</uri> ???<description/> ??</race> ?</races> </acme-races>

現(xiàn)在運(yùn)行這個(gè)測(cè)驗(yàn)當(dāng)然會(huì)引起一系列的失敗,因?yàn)檫€沒(méi)有實(shí)現(xiàn) RESTful 服務(wù)。同樣要注意,源代碼下載中的 Ant 構(gòu)建文件包含部署 WAR 文件和啟動(dòng)與停止 Tomcat 的任務(wù)(參見 下載)。這些都是成功運(yùn)行測(cè)試的必要條件。

結(jié)果證明滿足 GET 請(qǐng)求其實(shí)很簡(jiǎn)單。只需要對(duì) Race 域?qū)ο笳{(diào)用 findAll 方法,然后將該調(diào)用的結(jié)果傳入 RaceReporter 的 racesToXml() 方法。因此,您需要更新 RacesResource 實(shí)例,引入新的成員變量和構(gòu)造函數(shù)的新的初始化,如清單 20 所示:

清單 20. 不要忘記添加 RaceReporter
1 2 3 4 5 6 7 8 9 public class RacesResource extends Resource { ?private RaceReporter reporter; ?public RacesResource(Context context, Request request, Response response) { ??super(context, request, response); ??this.getVariants().add(new Variant(MediaType.TEXT_XML)); ??this.reporter = new RaceReporter(); ?} }

現(xiàn)在,實(shí)現(xiàn) GET 請(qǐng)求就很容易了。只要向 getRepresentation 方法中添加三行內(nèi)容就可以了,如清單 21 所示:

清單 21. 完成 GET request
1 2 3 4 5 public Representation getRepresentation(Variant variant) { ?Collection<Race> races = Race.findAll(); ?String xml = this.reporter.racesToXml(races); ?return new StringRepresentation(xml); }

信不信由您,僅此而已!

但是等等,不需要部署這個(gè)應(yīng)用程序來(lái)測(cè)試它么?

部署和驗(yàn)證

在實(shí)際測(cè)試 RESTful 服務(wù)是否可以返回一個(gè)比賽列表之前,您需要先部署應(yīng)用程序。本節(jié)展示了如何部署。

配置 web.xml

幸運(yùn)的是,部署 Restlet 應(yīng)用程序再簡(jiǎn)單不過(guò)了。您只要?jiǎng)?chuàng)建一個(gè)普通的 WAR 文件,確保 web.xml 文件適當(dāng)配置就可以了。

要讓 Restlet 應(yīng)用程序在 servlet 容器中正常發(fā)揮功能,一定要更新 web.xml 文件:

  • 正確載入應(yīng)用程序
  • 通過(guò)框架的自定義 servlet 傳遞所有需求

因此,您的 web.xml 文件應(yīng)該如清單 22 所示:

清單 22. 簡(jiǎn)單的 web.xml 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_ID" version="2.4" ?xmlns="http://java.sun.com/xml/ns/j2ee" ?xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ?xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee ???http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> ?<display-name>RESTful racing</display-name> ?<context-param> ??<param-name>org.restlet.application</param-name> ??<param-value>RaceApplication</param-value> ?</context-param> ?<servlet> ??<servlet-name>RestletServlet</servlet-name> ??<servlet-class>com.noelios.restlet.ext.servlet.ServerServlet</servlet-class> ?</servlet> ?<servlet-mapping> ??<servlet-name>RestletServlet</servlet-name> ??<url-pattern>/*</url-pattern> ?</servlet-mapping> </web-app>

本教程的源代碼(參見 下載)含有一個(gè) war 任務(wù),它會(huì)自動(dòng)構(gòu)建 WAR 文件,以及支持將 WAR 文件部署到 Tomcat 本地實(shí)例的構(gòu)建文件。

可以看到,在清單 22 中的第一部分,org.restlet.application 搭配 Restlet 應(yīng)用程序的類名 RaceApplication(如果要賦予它一個(gè)包名的話,可能需要完全限定那個(gè)名稱)。也要注意,文檔最后的部分將所有請(qǐng)求映射到 RestletServlet 類型,該類型先前已被映射到了 com.noelios.restlet.ext.servlet.ServerServlet 類。

RESTfully 測(cè)試

現(xiàn)在測(cè)試 RESTful Web 服務(wù)只要重新運(yùn)行 清單 18 中的測(cè)驗(yàn)用例。

再看一下測(cè)試,可以解釋一些東西。Restlet 的 Client 對(duì)象支持基本的 HTTP 命令 GETPUT、POST 和 DELETE。Client 對(duì)象可以采取不同的協(xié)議的形式 — 在這種情況下只會(huì)偶爾依賴 HTTP。

GET 請(qǐng)求已經(jīng)奏效了(參見圖 1),所以你可以寫另外一個(gè)測(cè)試。這一次,要完成 POST 所有期望的行為;換句話說(shuō),通過(guò) RacesResource 類測(cè)試新比賽的創(chuàng)建。

圖 1. 在瀏覽器中查看 RESTful GET 請(qǐng)求

要測(cè)試 POST,您需要用相關(guān)的信息來(lái)形成一個(gè) XML 請(qǐng)求文檔,并確保服務(wù)成功發(fā)回響應(yīng)。當(dāng)然,事實(shí)證明寫這樣的一個(gè)測(cè)試是相當(dāng)簡(jiǎn)單的。只需向現(xiàn)有 JUnit 類添加一些額外的代碼,如清單 23 所示:

清單 23. createRace 測(cè)驗(yàn)用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private static String raceXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + ?"<acme-races>\n" + ?" <race name='Limerick 2008 Half' date='2008-05-12' distance='13.4'>\n" + ?" <description>erin go brach</description>\n" + ?" </race>\n" + ?"</acme-races>"; @Test public void createRace() { ?Form form = new Form(); ?form.add("data", this.raceXML); ?Representation rep = form.getWebRepresentation(); ?Client client = new Client(Protocol.HTTP); ?Response response = ??client.post("http://localhost:8080/racerrest/race/", rep); ?assertTrue(response.getStatus().isSuccess());

正如您所看到的,清單 23 迅速形成了一個(gè)表示 XML 文檔的 String。在這里,我在創(chuàng)建一個(gè)稱為 Limerick 2008 Half 的新比賽。然后它用 Restlet 框架的 Client 對(duì)象將這個(gè)文檔傳給服務(wù)器。最后,它確保返回一個(gè)成功指示。

現(xiàn)在運(yùn)行測(cè)驗(yàn)。失敗了,是么?那是因?yàn)槟€沒(méi)有實(shí)現(xiàn) POST 請(qǐng)求代碼,這是下一小節(jié)的任務(wù)。

通過(guò) RESTful 服務(wù)創(chuàng)建比賽

通過(guò) RESTful Web 服務(wù)創(chuàng)建比賽只需要幾個(gè)步驟就可以完成:接受 XML 文檔、解析它、在底層數(shù)據(jù)庫(kù)中創(chuàng)建一個(gè)新的 Race 實(shí)例,最后返回一個(gè)表示事務(wù)結(jié)果的響應(yīng)。本小節(jié)涵蓋了這幾步。

處理 POST 請(qǐng)求

要通過(guò) REST 實(shí)現(xiàn)創(chuàng)建行為,需要邏輯處理 POST 請(qǐng)求。因此,在 RacesResource 類中,必須覆蓋兩個(gè)方法:allowPost() 和 post()。

post() 方法完成這里的所有工作。它采用了一個(gè) Representation 實(shí)例,可以從該實(shí)例中獲取傳送的數(shù)據(jù)。回憶一下 清單 23 中的 createRace 測(cè)試用例將 XML 文檔與一個(gè)名稱相關(guān)聯(lián):data。因此,通過(guò) Restlet 框架的 Form 對(duì)象,可以獲取一個(gè)表示傳入的 XML 的 String,然后您可以將它傳入所提供的 RaceConsumer 對(duì)象。這個(gè)對(duì)象很容易接受 XML 文檔,并會(huì)相應(yīng)地操作底層數(shù)據(jù)庫(kù)。

如果事務(wù)有效的話,然后您將要相應(yīng)地用一個(gè)成功的響應(yīng)進(jìn)行回應(yīng);無(wú)效的話,你將需要回應(yīng)一個(gè)失敗消息。

繼續(xù)并覆蓋 allowPost() 和 post(),如清單 24 所示:

清單 24. 覆蓋 POST 方法
1 2 3 4 5 public boolean allowPost() { ?return true; } public void post(Representation representation) {}

由于您將使用 RaceConsumer 對(duì)象,所以要將其添加為 RacesResource 類的一個(gè)成員變量,并在構(gòu)造函數(shù)中將其初始化。相應(yīng)地更新對(duì)象,如清單 25 所示:

清單 25. 添加 RaceConsumer 類
1 2 3 4 5 6 7 8 9 10 11 public class RacesResource extends Resource { ?private RaceReporter reporter; ?private RaceConsumer consumer; ?public RacesResource(Context context, Request request, Response response) { ??super(context, request, response); ??this.getVariants().add(new Variant(MediaType.TEXT_XML)); ??this.reporter = new RaceReporter(); ??this.consumer = new RaceConsumer(); ?} }

接下來(lái),確保您的 post() 方法如清單 26 所示:

清單 26. 實(shí)現(xiàn) post()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void post(Representation representation) { ?Form form = new Form(representation); ?String raceXML = form.getFirstValue("data"); ?Representation rep = null; ?try { ??long id = this.consumer.createRace(raceXML); ??getResponse().setStatus(Status.SUCCESS_CREATED); ??rep = new StringRepresentation(raceXML, MediaType.TEXT_XML); ??rep.setIdentifier(getRequest().getResourceRef().getIdentifier() + id); ?} catch (Throwable thr) { ??getResponse().setStatus(Status.SERVER_ERROR_INTERNAL); ??rep = new StringRepresentation("there was an error creating the race", ????MediaType.TEXT_PLAIN); ?} ?getResponse().setEntity(rep); }

正如您所看到的,post 方法內(nèi)容很多;然而,仔細(xì)檢查之后,沒(méi)有看上去那么復(fù)雜。傳入的 XML 是通過(guò) Form 對(duì)象獲取的。然后 XML(以一個(gè) String 的形式)會(huì)被傳入 consumer 實(shí)例的 createRace() 方法。如果一切都奏效的話(換句話說(shuō),比賽被持久化),會(huì)生成一個(gè)包含成功狀態(tài)的響應(yīng),然后重新散列傳入的 XML 及由此而生的 URI(即,race/43,此處 43 為新創(chuàng)建的比賽的 id)。

如果進(jìn)展得不是很順利的話,本質(zhì)上會(huì)是相同的過(guò)程,不同之處是失敗消息會(huì)返回失敗狀態(tài):創(chuàng)建失敗,無(wú) URI 返回。

繼續(xù)并重新運(yùn)行 createRace 測(cè)試。假設(shè)您已經(jīng)重新部署了 RESTful Web 應(yīng)用程序,一切都會(huì)順利運(yùn)行。

結(jié)束語(yǔ)

本教程實(shí)現(xiàn)的僅僅是中等程度的 Acme Racing 需求。但在此過(guò)程中,您已經(jīng)了解到了使用 Restlets 是很方便的。整個(gè)過(guò)程中最困難的部分就是實(shí)現(xiàn)邏輯 RESTful API。本教程的源代碼為提高您的學(xué)習(xí)樂(lè)趣提供了大量特性(參見 下載)。

詩(shī)人亞歷山大·蒲柏曾說(shuō)過(guò) “保持簡(jiǎn)潔要遠(yuǎn)遠(yuǎn)優(yōu)于費(fèi)盡心思營(yíng)造出來(lái)的復(fù)雜。” 對(duì)于 REST 來(lái)說(shuō)這句話在合適不過(guò)了。記住 REST 是一種思維方式 — 是設(shè)計(jì)基于命名資源而非消息的松耦合應(yīng)用程序的一種風(fēng)格。通過(guò)借助已經(jīng)驗(yàn)證了的成功的 Web 基礎(chǔ)設(shè)施,REST 使得這些應(yīng)用程序易于設(shè)計(jì)和實(shí)現(xiàn)。而且 REST 應(yīng)用程序具有良好的擴(kuò)展性。

本教程只涉及到了 Restlet 框架特性的冰山一角,但不要讓它欺騙了您。這個(gè)框架可以做很多事,包括在需要時(shí)添加安全設(shè)施。Restlet 是一種編碼享受,只要查看一些 Restlet 代碼,就可以很容易地理解代碼庫(kù)。

艾伯特愛(ài)因斯坦曾經(jīng)說(shuō)過(guò),“所有事物都應(yīng)該盡量保持簡(jiǎn)單,但不可過(guò)分簡(jiǎn)化。” 我認(rèn)為 Restlet 框架和 REST 本身例證了這個(gè)名言的智慧,希望您可以同意我的看法。

下載資源


總結(jié)

以上是生活随笔為你收集整理的构建 RESTful Web 服务的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。