首先簡要介紹一下REST。REST代表Representational State Transfer,它是World Wide Web所依賴的一套架構原則。Roy Fielding在他的博士論文“Architectural Styles and the Design of Network-based Software Architectures”中首次提出了這個概念。在他的論文中,Fielding明確指出REST和World Wide Web的五個架構原則:
- 可尋址性(Addressability)。REST中的所有東西都基于資源 的概念。資源與OOP中的對象或其他名詞不同,它是一種抽象,必須可以通過 URI 尋址或訪問。
接口一致性(Interface uniformity)。與SOAP或其他標準不同,REST 要求用來操縱資源的方法或動詞不是任意的。這意味著RESTful服務的開發人員只能使用HTTP支持的方法,比如GET、PUT、POST、DELETE等等。因此不需要使用 WSDL 等服務描述語言。
- 無狀態(Statelessness)。為了增強可伸縮性,服務器端不存儲客戶機的狀態信息。這使服務器不與特定的客戶機相綁定,負載平衡變得簡單多了。這還讓服務器更容易監視、更可靠。
- 具象(Representational)。客戶機總是與資源的某種具象交互,絕不會直接與資源本身交互。同一資源還可以有多個具象。理論上說,持有資源的具象的任何客戶機應該有操縱底層資源的足夠信息。
- 連通性(Connectedness)。任何基于REST的系統都應該預見到客戶機需要訪問相關的資源,應該在返回的資源具象中包含這些資源。例如,可以以超鏈接的形式包含特定RESTful服務的操作序列中的相關步驟,讓客戶機可以根據需要訪問它們。
JAX-RS
Web上的REST以下站點當前正在使用REST:
Atom Publishing Protocol。Atom是REST協議最正規的實現之一,廣泛用于博客發布領域。
Sun的Cloud API。這是Sun的RESTful API,用于管理和創建計算、連網和存儲元素等云資源。
Digg的API。Digg 是一個流行的社交網站,它使用RESTful API讓用戶和合作伙伴能夠以編程方式與站點和數據交互。
Netflix API。Netflix是一個DVD出租網站,它使用RESTful API提供對影片目錄的細粒度訪問,以及以編程方式調整用戶隊列和獲取影片推薦。
Flickr API。Flickr是一個照片上傳網站,用戶可以使用它提供的RESTful API上傳、更換和搜索照片和像冊。
為什么需要另一個Java標準?定義JAX-RS這個新規范是為了簡化基于REST的Java開發。它主要關注使用Java注釋和普通舊式Java對象(POJO)實現 RESTful 服務。盡管總是可以使用servlet實現RESTful服務,但是以這種方式實現業務邏輯需要太多HTTP GET請求。
JAX-RS隱藏所有 HTTP并把servlet綁定到Java類中的各個方法。注釋還可以動態地提取HTTP請求中的信息,以及把應用程序生成的異常映射到HTTP響應碼。由于這些原因,JAX-RS是一種實現RESTful Java Web服務的有效方法。
Apache Wink和REST
我已經介紹了REST和JAX-RS,現在開始討論Apache Wink。Apache Wink 1.0是一個從頭設計的完全兼容的JAX-RS 1.0規范實現。它很容易使用和應用于生產環境,它提供的特性可以增強核心JAX-RS規范。
Apache Wink運行時架構是JAX-RS 1.0規范的簡單實現。Apache Wink部署在Java Platform, Enterprise Edition (Java EE)環境中,由以下三個組件組成:
- Apache Wink RestServlet。RestServlet在Web應用程序的Java EE web.xml描述符文件中配置。這個servlet作為所有HTTP Web服務請求的主入口點,它把請求和響應對象實例分派給請求處理器進行進一步處理。
- 請求處理器。RequestProcessor是核心Apache Wink引擎,它由Apache Wink RestServlet初始化。請求處理器使用請求 URI 尋找、匹配和調用相應的資源類和方法。在請求處理期間發生的任何異常都會導致RequestProcessor調用Error Handler Chain以處理異常。
- 資源。在REST中,代表Web服務的任何組件或對象都被稱為資源。資源允許通過它的許多具象之一獲取和操縱數據。實現資源的POJO被稱為資源類。資源類進一步實現資源方法,資源方法實際處理底層業務邏輯。
- 整個請求周期被稱為Apache Wink邏輯流,見 圖 1。

圖 1. Apache Wink 邏輯流
Apache Wink不但幫助實現RESTful Web服務,而且提供一個強大的客戶機庫,可以使用它輕松地消費RESTful服務。最后,Apache Wink附帶一組內置的提供者,它們幫助開發人員支持多種行業標準的數據格式:XML、Atom、RSS、JSON、CSV和HTML。
RESTful設計
現在該實踐一下了。為了保持趣味性,我們要在Apache Wink 1.0上設計、實現和部署一個不太簡單的RESTful服務。這個服務是PayPal Payflow支付網關服務的RESTful包裝器,它可以通過Internet進行信用卡處理。但是,對于這個示例,我們只關注它的交易查詢功能。只要提供一個屬于經過身份驗證的用戶的惟一ID,這個功能可以查詢任何交易的狀態。
資源/URI設計
首先定義服務的接口模型并給它分配URI,這使服務成為REST中的資源。因為這個服務的功能是提供交易狀態,可以公開 清單 1 所示的 URI。
清單 1. 交易服務的URI模式
以下是引用片段:
/transactions
/transactions{id} |
transactions URI表示系統中的所有交易。使用/transactions{id}查詢某一交易的狀態。{id}代表對應于交易模型的交易ID的惟一字母數字值。另外,使用 清單2中的模式驗證用戶的身份,其中的UNAME、VNAME、PNAME和PWD是在注冊時分配給商人的Payflow網關登錄憑證的組成部分。
清單 2. 在查詢字符串中包含用戶憑證的URI模式
以下是引用片段:
/transactions{id}?user=UNAME&vendor=VNAME&partner=PNAME&pwd=PWD |
數據設計
每個RESTful接口必須決定支持它的客戶機使用哪種具象。Apache Wink 1.0可以支持XML、JSON、HTML和Atom 等,這個示例使用JSON,因為這是一種流行的格式,而且Ajax應用程序很容易使用JavaScript代碼。清單 3 是一個采用JSON格式的交易狀態示例。
清單 3. JSON字符串形式的交易狀態響應
以下是引用片段:
{
"RESULT":19,"PNREF":"V19A2A1A7CC5",
"RESPMSG":"Original transaction ID not found: V19A2A192BE9",
"AUTHCODE":null,"CVV2MATCH":null,"AVSADDR":null,
"AVSZIP":null,"IAVS":null,"CARDSECURE":null
} |
HTTP方法設計
最后,必須決定使用哪些HTTP方法操作資源及其功能。一定要堅持這些HTTP方法的正規用法,不要偏離規范。例如,GET應該是安全的只讀冪等(idempotent)調用,它不應該以任何方式更改資源的狀態。違反這條原則會增加復雜性,給客戶機帶來混亂。在這個示例中,因為希望對單一交易的狀態執行只讀查詢,顯然應該使用GET方法,使用的URI模式見 清單 4。
清單 4. 交易服務示例URI模式
以下是引用片段:
/transactions{id} |
每個GET調用返回一個JSON格式的字符串,這是查詢的交易的狀態數據,見 清單 5。
清單 5. 包含交易ID的GET請求
以下是引用片段:
GET /transactions/V19A2A192BE9 HTTP/1.1 |
但是,這種查詢交易狀態的模型有一個問題:服務無法驗證查詢交易的用戶是否確實是交易的所有者。為了解決這個問題,允許客戶機在URI中作為查詢參數傳遞登錄憑證,見 清單 6。
清單 6. 在查詢字符串中包含安全憑證的GET請求
以下是引用片段:
GET /transactions/V19A2A192BE9?user=winktest&vendor=winktest
&partner=PayPal&pwd=wink123
HTTP/1.1 |
Apache Wink服務實現
Apache Wink服務實現為POJO(即普通的Java類),它使用JAX-RS注釋把HTTP請求映射到Java方法。在默認情況下,服務可以是單實例的,也可以為每個請求創建服務實例。在這個示例中,通過創建TransactionResource類實現Apache Wink RESTful服務。它解析狀態查詢請求,驗證用戶憑證,調用Payflow網關服務,然后以JSON對象的形式返回交易的狀態。清單 7 給出TransactionResource類的代碼片段。
清單 7. Apache Wink服務Java類片段
以下是引用片段:
package org.openengine.wink.example.payflow;
import ...;
@Path("/transactions")
public class TransactionResource {
@GET
@Path("{pnref}")
@Produces(MediaType.APPLICATION_JSON)
public JSONObject doInquiry(@PathParam("pnref") String pnref,
@QueryParam("user") String userName,
@QueryParam("vendor") String vendorName,
@QueryParam("partner") String partnerName,
@QueryParam("pwd") String password) {
try {
return getTxnStatus(pnref,
userName, vendorName,
partnerName, password);
} catch (JSONException e) {
throw new WebApplicationException
(Response.Status.INTERNAL_SERVER_ERROR);
}
}
.... |
@javax.ws.rs.Path注釋指出這個類是JAX-RS服務。所有JAX-RS服務都需要這個注釋。@Path 注釋的值 /transactions 指出交易服務的URI的相對路徑。
@GET注釋指出這個方法本身映射到的HTTP動詞。方法級@Path注釋的值相對于主URI指定子根。方法級@Path和@PathParam中的{pnref}表示交易的惟一ID。例如,如果URI是 /transactions/V19A2A192BE9,V19A2A192BE9字符串會被注入getTxnStatus方法的{pnref}參數中。
@javax.ws.rs.QueryParam 注釋與@PathParam相似,但是它把各個URI查詢參數注入Java參數中,見 清單 8。
清單 8. 包含查詢參數的URI
以下是引用片段:
/transactions/V19A2A192BE9?user=winktest&vendor=winktest&partner=PayPal&pwd=wink123 |
Apache Wink服務配置
Apache Wink應用程序通常打包為WAR文件,部署在Apache Tomcat等servlet容器中。與其他Web應用程序一樣,Apache Wink服務也需要一個web.xml文件。清單 9 給出示例Apache Wink服務的web.xml文件的內容。
清單 9. web.xml Web配置文件
以下是引用片段:
<web-app>
<display-name>Wink demo</display-name>
<description>Demonstration of SDK features</description>
<!-- Wink SDK servlet configuration.
This servlet handles HTTP requests
of SDK web service on application server.-->
<servlet>
<servlet-name>restSdkService</servlet-name>
<servlet-class>
org.apache.wink.server.internal.servlet.RestServlet
</servlet-class>
<init-param>
<param-name>applicationConfigLocation</param-name>
<param-value>/WEB-INF/application</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>restSdkService</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
</web-app> |
可以看到web.xml文件中定義了Apache Wink RestServlet和它的url-pattern。還有RestServlet的初始化參數,它指向/WEB-INF/application目錄中的一個文件。這個文件包含JAX-RS應該部署的所有類和對象的列表。您不需要這個配置文件,可以讓應用程序類以編程方式列出服務實現的資源,但是這個示例使用應用程序配置文件的方式,見 清單10。
清單 10. 應用程序配置文件
以下是引用片段:
org.openengine.wink.example.payflow.TransactionResource |
運行Apache Wink服務
要想運行這個服務,首先必須在Tomcat上構建和部署應用程序。執行以下步驟:
把PayFlow項目的內容解壓到C驅動器上的一個文件夾。C:\PayFlow目錄應該像 圖 2 這樣。

圖 2. PayFlow 示例項目的目錄結構
把JAVA_HOME和ANT_HOME變量設置為自己的Java和Apache Ant安裝目錄。
在系統變量PATH中添加JAVA_HOME/bin 和 ANT_HOME/bin。
從C:\PayFlow目錄運行Ant,從而對PayFlow項目執行Ant構建。
這個步驟應該會構建PayFlow項目并創建文件PayFlow.war,見 圖 3。

圖 3. Ant 腳本構建的輸出
把PayFlow.war文件復制到TOMCAT主目錄下的webapps目錄中,見 圖 4。

圖 4. 在 Tomcat 服務器上部署 PayFlow 項目
通過運行TOMCAT_HOME\bin文件夾中的startup.bat文件啟動Tomcat Web服務器。
啟動您喜歡的瀏覽器,訪問URI http://localhost:8080/PayFlow/rest/transactions/V19A2A192BE9?
user=winktest&vendor=winktest&partner=PayPal&pwd=wink123>(假設Tomcat Web服務器配置為監聽端口8080)。
瀏覽器應該會提示您保存文件 V19A2A192BE9,其中包含JSON格式的交易狀態,見 圖 5。

圖 5. 瀏覽器保存文件窗口,保存包含交易狀態的JSON文件
結束語
本文簡要介紹了REST架構和JAX-RS Java標準(這個標準用于簡化RESTful服務實現)的基本知識。然后討論了新的Apache Wink 1.0框架,這是一個完全兼容的容易使用的JAX-RS 1.0規范實現。介紹了Apache Wink的底層架構,然后設計和實現了一個新的示例Apache Wink Web服務,討論了部署和運行它所需的步驟。