首先簡要介紹一下REST。REST代表Representational State Transfer,它是World Wide Web所依賴的一套架構(gòu)原則。Roy Fielding在他的博士論文“Architectural Styles and the Design of Network-based Software Architectures”中首次提出了這個概念。在他的論文中,F(xiàn)ielding明確指出REST和World Wide Web的五個架構(gòu)原則:
- 可尋址性(Addressability)。REST中的所有東西都基于資源 的概念。資源與OOP中的對象或其他名詞不同,它是一種抽象,必須可以通過 URI 尋址或訪問。
接口一致性(Interface uniformity)。與SOAP或其他標(biāo)準不同,REST 要求用來操縱資源的方法或動詞不是任意的。這意味著RESTful服務(wù)的開發(fā)人員只能使用HTTP支持的方法,比如GET、PUT、POST、DELETE等等。因此不需要使用 WSDL 等服務(wù)描述語言。 - 無狀態(tài)(Statelessness)。為了增強可伸縮性,服務(wù)器端不存儲客戶機的狀態(tài)信息。這使服務(wù)器不與特定的客戶機相綁定,負載平衡變得簡單多了。這還讓服務(wù)器更容易監(jiān)視、更可靠。
- 具象(Representational)。客戶機總是與資源的某種具象交互,絕不會直接與資源本身交互。同一資源還可以有多個具象。理論上說,持有資源的具象的任何客戶機應(yīng)該有操縱底層資源的足夠信息。
- 連通性(Connectedness)。任何基于REST的系統(tǒng)都應(yīng)該預(yù)見到客戶機需要訪問相關(guān)的資源,應(yīng)該在返回的資源具象中包含這些資源。例如,可以以超鏈接的形式包含特定RESTful服務(wù)的操作序列中的相關(guān)步驟,讓客戶機可以根據(jù)需要訪問它們。
JAX-RS
Web上的REST以下站點當(dāng)前正在使用REST:
Atom Publishing Protocol。Atom是REST協(xié)議最正規(guī)的實現(xiàn)之一,廣泛用于博客發(fā)布領(lǐng)域。
Sun的Cloud API。這是Sun的RESTful API,用于管理和創(chuàng)建計算、連網(wǎng)和存儲元素等云資源。
Digg的API。Digg 是一個流行的社交網(wǎng)站,它使用RESTful API讓用戶和合作伙伴能夠以編程方式與站點和數(shù)據(jù)交互。
Netflix API。Netflix是一個DVD出租網(wǎng)站,它使用RESTful API提供對影片目錄的細粒度訪問,以及以編程方式調(diào)整用戶隊列和獲取影片推薦。
Flickr API。Flickr是一個照片上傳網(wǎng)站,用戶可以使用它提供的RESTful API上傳、更換和搜索照片和像冊。
為什么需要另一個Java標(biāo)準?定義JAX-RS這個新規(guī)范是為了簡化基于REST的Java開發(fā)。它主要關(guān)注使用Java注釋和普通舊式Java對象(POJO)實現(xiàn) RESTful 服務(wù)。盡管總是可以使用servlet實現(xiàn)RESTful服務(wù),但是以這種方式實現(xiàn)業(yè)務(wù)邏輯需要太多HTTP GET請求。
JAX-RS隱藏所有 HTTP并把servlet綁定到Java類中的各個方法。注釋還可以動態(tài)地提取HTTP請求中的信息,以及把應(yīng)用程序生成的異常映射到HTTP響應(yīng)碼。由于這些原因,JAX-RS是一種實現(xiàn)RESTful Java Web服務(wù)的有效方法。
Apache Wink和REST
我已經(jīng)介紹了REST和JAX-RS,現(xiàn)在開始討論Apache Wink。Apache Wink 1.0是一個從頭設(shè)計的完全兼容的JAX-RS 1.0規(guī)范實現(xiàn)。它很容易使用和應(yīng)用于生產(chǎn)環(huán)境,它提供的特性可以增強核心JAX-RS規(guī)范。
Apache Wink運行時架構(gòu)是JAX-RS 1.0規(guī)范的簡單實現(xiàn)。Apache Wink部署在Java Platform, Enterprise Edition (Java EE)環(huán)境中,由以下三個組件組成:
- Apache Wink RestServlet。RestServlet在Web應(yīng)用程序的Java EE web.xml描述符文件中配置。這個servlet作為所有HTTP Web服務(wù)請求的主入口點,它把請求和響應(yīng)對象實例分派給請求處理器進行進一步處理。
- 請求處理器。RequestProcessor是核心Apache Wink引擎,它由Apache Wink RestServlet初始化。請求處理器使用請求 URI 尋找、匹配和調(diào)用相應(yīng)的資源類和方法。在請求處理期間發(fā)生的任何異常都會導(dǎo)致RequestProcessor調(diào)用Error Handler Chain以處理異常。
- 資源。在REST中,代表Web服務(wù)的任何組件或?qū)ο蠖急环Q為資源。資源允許通過它的許多具象之一獲取和操縱數(shù)據(jù)。實現(xiàn)資源的POJO被稱為資源類。資源類進一步實現(xiàn)資源方法,資源方法實際處理底層業(yè)務(wù)邏輯。
- 整個請求周期被稱為Apache Wink邏輯流,見 圖 1。
圖 1. Apache Wink 邏輯流
Apache Wink不但幫助實現(xiàn)RESTful Web服務(wù),而且提供一個強大的客戶機庫,可以使用它輕松地消費RESTful服務(wù)。最后,Apache Wink附帶一組內(nèi)置的提供者,它們幫助開發(fā)人員支持多種行業(yè)標(biāo)準的數(shù)據(jù)格式:XML、Atom、RSS、JSON、CSV和HTML。
RESTful設(shè)計
現(xiàn)在該實踐一下了。為了保持趣味性,我們要在Apache Wink 1.0上設(shè)計、實現(xiàn)和部署一個不太簡單的RESTful服務(wù)。這個服務(wù)是PayPal Payflow支付網(wǎng)關(guān)服務(wù)的RESTful包裝器,它可以通過Internet進行信用卡處理。但是,對于這個示例,我們只關(guān)注它的交易查詢功能。只要提供一個屬于經(jīng)過身份驗證的用戶的惟一ID,這個功能可以查詢?nèi)魏谓灰椎臓顟B(tài)。
資源/URI設(shè)計
首先定義服務(wù)的接口模型并給它分配URI,這使服務(wù)成為REST中的資源。因為這個服務(wù)的功能是提供交易狀態(tài),可以公開 清單 1 所示的 URI。
清單 1. 交易服務(wù)的URI模式
以下是引用片段: /transactions /transactions{id} |
transactions URI表示系統(tǒng)中的所有交易。使用/transactions{id}查詢某一交易的狀態(tài)。{id}代表對應(yīng)于交易模型的交易ID的惟一字母數(shù)字值。另外,使用 清單2中的模式驗證用戶的身份,其中的UNAME、VNAME、PNAME和PWD是在注冊時分配給商人的Payflow網(wǎng)關(guān)登錄憑證的組成部分。
清單 2. 在查詢字符串中包含用戶憑證的URI模式
以下是引用片段: /transactions{id}?user=UNAME&vendor=VNAME&partner=PNAME&pwd=PWD |
數(shù)據(jù)設(shè)計
每個RESTful接口必須決定支持它的客戶機使用哪種具象。Apache Wink 1.0可以支持XML、JSON、HTML和Atom 等,這個示例使用JSON,因為這是一種流行的格式,而且Ajax應(yīng)用程序很容易使用JavaScript代碼。清單 3 是一個采用JSON格式的交易狀態(tài)示例。
清單 3. JSON字符串形式的交易狀態(tài)響應(yīng)
以下是引用片段: { "RESULT":19,"PNREF":"V19A2A1A7CC5", "RESPMSG":"Original transaction ID not found: V19A2A192BE9", "AUTHCODE":null,"CVV2MATCH":null,"AVSADDR":null, "AVSZIP":null,"IAVS":null,"CARDSECURE":null } |
HTTP方法設(shè)計
最后,必須決定使用哪些HTTP方法操作資源及其功能。一定要堅持這些HTTP方法的正規(guī)用法,不要偏離規(guī)范。例如,GET應(yīng)該是安全的只讀冪等(idempotent)調(diào)用,它不應(yīng)該以任何方式更改資源的狀態(tài)。違反這條原則會增加復(fù)雜性,給客戶機帶來混亂。在這個示例中,因為希望對單一交易的狀態(tài)執(zhí)行只讀查詢,顯然應(yīng)該使用GET方法,使用的URI模式見 清單 4。
清單 4. 交易服務(wù)示例URI模式
以下是引用片段: /transactions{id} |
每個GET調(diào)用返回一個JSON格式的字符串,這是查詢的交易的狀態(tài)數(shù)據(jù),見 清單 5。
清單 5. 包含交易ID的GET請求
以下是引用片段: GET /transactions/V19A2A192BE9 HTTP/1.1 |
但是,這種查詢交易狀態(tài)的模型有一個問題:服務(wù)無法驗證查詢交易的用戶是否確實是交易的所有者。為了解決這個問題,允許客戶機在URI中作為查詢參數(shù)傳遞登錄憑證,見 清單 6。
清單 6. 在查詢字符串中包含安全憑證的GET請求
以下是引用片段: GET /transactions/V19A2A192BE9?user=winktest&vendor=winktest &partner=PayPal&pwd=wink123 HTTP/1.1 |
Apache Wink服務(wù)實現(xiàn)
Apache Wink服務(wù)實現(xiàn)為POJO(即普通的Java類),它使用JAX-RS注釋把HTTP請求映射到Java方法。在默認情況下,服務(wù)可以是單實例的,也可以為每個請求創(chuàng)建服務(wù)實例。在這個示例中,通過創(chuàng)建TransactionResource類實現(xiàn)Apache Wink RESTful服務(wù)。它解析狀態(tài)查詢請求,驗證用戶憑證,調(diào)用Payflow網(wǎng)關(guān)服務(wù),然后以JSON對象的形式返回交易的狀態(tài)。清單 7 給出TransactionResource類的代碼片段。
清單 7. Apache Wink服務(wù)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服務(wù)。所有JAX-RS服務(wù)都需要這個注釋。@Path 注釋的值 /transactions 指出交易服務(wù)的URI的相對路徑。
@GET注釋指出這個方法本身映射到的HTTP動詞。方法級@Path注釋的值相對于主URI指定子根。方法級@Path和@PathParam中的{pnref}表示交易的惟一ID。例如,如果URI是 /transactions/V19A2A192BE9,V19A2A192BE9字符串會被注入getTxnStatus方法的{pnref}參數(shù)中。
@javax.ws.rs.QueryParam 注釋與@PathParam相似,但是它把各個URI查詢參數(shù)注入Java參數(shù)中,見 清單 8。
清單 8. 包含查詢參數(shù)的URI
以下是引用片段: /transactions/V19A2A192BE9?user=winktest&vendor=winktest&partner=PayPal&pwd=wink123 |
Apache Wink服務(wù)配置
Apache Wink應(yīng)用程序通常打包為WAR文件,部署在Apache Tomcat等servlet容器中。與其他Web應(yīng)用程序一樣,Apache Wink服務(wù)也需要一個web.xml文件。清單 9 給出示例Apache Wink服務(wù)的web.xml文件的內(nèi)容。
清單 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的初始化參數(shù),它指向/WEB-INF/application目錄中的一個文件。這個文件包含JAX-RS應(yīng)該部署的所有類和對象的列表。您不需要這個配置文件,可以讓應(yīng)用程序類以編程方式列出服務(wù)實現(xiàn)的資源,但是這個示例使用應(yīng)用程序配置文件的方式,見 清單10。
清單 10. 應(yīng)用程序配置文件
以下是引用片段: org.openengine.wink.example.payflow.TransactionResource |
運行Apache Wink服務(wù)
要想運行這個服務(wù),首先必須在Tomcat上構(gòu)建和部署應(yīng)用程序。執(zhí)行以下步驟:
把PayFlow項目的內(nèi)容解壓到C驅(qū)動器上的一個文件夾。C:\PayFlow目錄應(yīng)該像 圖 2 這樣。
圖 2. PayFlow 示例項目的目錄結(jié)構(gòu)
把JAVA_HOME和ANT_HOME變量設(shè)置為自己的Java和Apache Ant安裝目錄。
在系統(tǒng)變量PATH中添加JAVA_HOME/bin 和 ANT_HOME/bin。
從C:\PayFlow目錄運行Ant,從而對PayFlow項目執(zhí)行Ant構(gòu)建。
這個步驟應(yīng)該會構(gòu)建PayFlow項目并創(chuàng)建文件PayFlow.war,見 圖 3。
圖 3. Ant 腳本構(gòu)建的輸出
把PayFlow.war文件復(fù)制到TOMCAT主目錄下的webapps目錄中,見 圖 4。
圖 4. 在 Tomcat 服務(wù)器上部署 PayFlow 項目
通過運行TOMCAT_HOME\bin文件夾中的startup.bat文件啟動Tomcat Web服務(wù)器。
啟動您喜歡的瀏覽器,訪問URI http://localhost:8080/PayFlow/rest/transactions/V19A2A192BE9?
user=winktest&vendor=winktest&partner=PayPal&pwd=wink123>(假設(shè)Tomcat Web服務(wù)器配置為監(jiān)聽端口8080)。
瀏覽器應(yīng)該會提示您保存文件 V19A2A192BE9,其中包含JSON格式的交易狀態(tài),見 圖 5。
圖 5. 瀏覽器保存文件窗口,保存包含交易狀態(tài)的JSON文件
結(jié)束語
本文簡要介紹了REST架構(gòu)和JAX-RS Java標(biāo)準(這個標(biāo)準用于簡化RESTful服務(wù)實現(xiàn))的基本知識。然后討論了新的Apache Wink 1.0框架,這是一個完全兼容的容易使用的JAX-RS 1.0規(guī)范實現(xiàn)。介紹了Apache Wink的底層架構(gòu),然后設(shè)計和實現(xiàn)了一個新的示例Apache Wink Web服務(wù),討論了部署和運行它所需的步驟。