隨著 Web 2.0 的 發展,REST(Representational State Transfer)風格的 Web Service 得到普遍的應用,各種 REST 框架如雨後春筍般發展起來。當我們從事 Web 2.0 的實踐時,越來越感覺到服務器端的 REST 服務在應對 MIS 一樣的信息系統以及和數據庫的交互時還存在很大的問題,比如效率低下,重復編碼等。
例如,我們假定如下的場景:需要使用 Web 2.0 的技術來實現一個信息管理系統,這個系統的主要的任務就是對後台數據庫的業務數據的操作,管理和報表。這樣的場景在如財務等這樣的業務系統中非常常見。我們可以使用實體 Bean 實現 EJB,再將該 EJB 包裝成為 REST 服務,或者使用輕量級的 Hibernate,配合各種 Java 的 REST 框架來實現,但是無論如何都不可避免需要做很多的重復編碼,或者要實現大量的數據庫操作。既然 REST 將服務看成是資源的服務,那麼我們可以認為數據庫中的數據也是一種資源。有沒有方法直接將它們轉化為 REST 服務呢?sqlRest 正是這樣一種框架,它是一種高效的輕量級數據庫 REST 服務解決方案,可以通過簡單配置直接將數據庫中的數據暴露成 REST 風格的 Web 服務,並將數據庫的 CRUD 操作和 REST 服務的 GET, DELETE, POST, PUT 接口對應起來。
本文將介紹這一框架的配置和運行,並討論如何測試 REST 服務,最後給出了配置 DB2 實現 DB2 中的資源 REST 化的參考辦法,相信會對 Web 2.0 和 REST 服務的開發者帶來一定的參考價值。本文的示例基於 Eclipse 3.2 和 JDK1.5,Servlet 容器使用的是 Tomcat 5.5 。
sqlRest 第一步
下載和運行
sqlRest 是 sourceforge 站點上的一個開源項目,它使用一個 Servlet 實現了基於數據庫的 REST 引擎,可以自動的將數據庫的表映射成 REST 資源,用戶可以使用 URL 獲取表的內容 (XML 格式),並進行相關操作。
我們首先下載一個二進制的包來運行 sqlRest 示例。您可以通過 http://sourceforge.net/projects/sqlrest/ 訪問該項目。在首頁上點擊下載,進入下載頁面下載 sqlrest-0.3.2 版本,這也是目前唯一可用的版本。
下載後解壓縮,請先確認安裝了 JRE 1.4.1 和 TOMCAT 4.1 以上版本。將解壓縮目錄內 webapps 目錄下的 sqlrest 目錄拷貝到 TOMCAT 的 webapps 目錄中。啟動 TOMCAT,在您的浏覽器內鍵入 http://localhost:8080/sqlrest, (如果您的 TOMCAT 的端口是 8080),您將看見如圖 1 的 sqlRest 的 Web 界面:
圖 1. sqlRest 初始 Web 界面
該頁面描述了數據庫中有 4 種可用的 REST 資源,這說明安裝成功,我們也就可以開始進一步的工作。
這裡讀者需要注意,根據 JRE 版本和類型的不同,可能在啟動的時候 TOMCAT 會出錯(參考如圖 2 的錯誤信息),sqlRest 的 Servlet 無法加載,造成錯誤的原因是我們下載的 sqlRest 是二進制版本,而編譯 sqlRest 的 JRE 和我們運行 Tomcat 的 JRE 之間存在不兼容。解決方法是獲取 sqlRest 的源碼,在運行環境中重新編譯,再將編譯的 class 文件拷貝到 Tomcat 中的 webapp/sqlrest/WEB-INF 目錄中的 classes 目錄下重新啟動就可以了。如果選擇從源碼項目開始編譯和部署運行,就可以避免這一錯誤(TOMCAT 和 Eclipse 使用的是同一 JRE),下面您將了解到如何從 CVS 獲取代碼並從源碼開始編譯部署 sqlRest 。
圖 2. 不同的 JDK 的兼容性問題引起的啟動錯誤
從源碼運行
sqlRest 在 SourceForge 上的源代碼可以通過匿名的 CVS 獲取。請先按圖 3 的配置方法在 Eclipse 中建立源碼倉庫。
圖 3. sqlRest 的源碼倉庫的 CVS 配置
檢出源碼項目後,我們發現這是一個 Java 項目,可以使用 ANT 的腳本構建出 Web 部署包。但是為了使用和測試方便,我們需要使用 Eclipse 的動態 Web 項目來調試 sqlRest 。此時需要根據已下載的 sqlRest 的二進制包從該 Java 項目的源碼創建一個 Eclipse 的 Web 項目(請確認安裝了 WTP 插件,筆者使用的是 WTP1.5 版本),本文以下的示例都是基於該 Web 項目的,筆者使用的開發環境是 Eclipse 3.2 和 JDK 1.5,Servlet 容器使用的是 TOMCAT 5.5 。我們創建的 Web 項目名為 sqlrest,讀者可以參考圖 4 中的項目結構:
圖 4. sqlRest 在 Eclipse 中的 Web Project
讀者也可以從附件中下載該 project 並導入到 Eclipse 的工作區。當在 Eclipse 中配置好 TOMCAT 服務器後,我們就可以進行進一步的開發和部署。請注意該 Web 項目的 properties 對話框,您需要將 java 編譯的結果輸出到 WEB-INF 目錄下的 classes 目錄,如圖 5 所示:
圖 5. 配置 Java class 的輸出
編譯後部署該項目,訪問 http://localhost:8080/sqlrest,您依然可以看見和圖 1 相同的 Web 界面
數據庫資源的配置
現在回顧一下圖 1 的內容, sqlRest 的示例中包含了 Customer,Product,Invoice 和 Item 4 種 Rest 資源,這四種資源對應著數據庫種的 4 張表,sqlRest 會自動的將數據庫的記錄作為資源通過 RestURL 和服務調用展示出來。現在您可以在浏覽器中嘗試如下的 URL 來測試這些 Rest 服務。
表 1. 在浏覽器中測試 sqlRest 的各種 URL)
在浏覽器中鍵入 REST URL 含義 http://localhost:8080/sqlrest 獲取配置數據庫的所有資源列表 http://localhost:8080/sqlrest/INVOICE/ 獲取所有的 invoice 資源列表 http://localhost:8080/sqlrest/INVOICE/31/ 獲取 id 號為 31 的 invoice 資源 http://localhost:8080/sqlrest/CUSTOMER/22/ 獲取 id 號為 22 的 customer 資源
我們知道在浏覽器中鍵入 URL 並回車,浏覽器會發出 GET 方法的 HTTP 請求,如果使用的是 REST 風格的 URL(且滿足服務器端的約定,因為 REST 是一種風格,而不是標准),並且服務器端提供了對應的 REST 服務,此時浏覽器相當於在執行 REST 服務的調用,浏覽器頁面中的內容就是 REST 服務的返回,也就是該 REST URL 對應的 REST 資源的內容。現在您一定很想知道,這些數據是從哪裡來的?顯然應該來自數據庫,它們應該就是 sqlRest 自帶的測試數據庫中的數據。下面我們將介紹 sqlRest 數據庫的配置,通過簡單的配置,sqlRest 可以支持直接將數據庫的數據資源暴露成 Rest Web 服務接口。
在我們的 Web 項目中,可以發現 WEB-INF 目錄下的 sqlrestconf.xml 文件,該配置文件描述了數據庫的信息和登錄信息,我們如果打開 WEB-INF\lib 目錄,我們將發現數據庫驅動程序:hsqldb-1-7-1.jar,根據配置文件我們就得知 sqlRest 默認使用的是 hypersonic 數據庫,一種嵌入式的 java 數據庫。那麼測試使用的數據源到底在哪裡呢?
研究源碼我們發現,MainServlet 類就是 sqlRest 服務的入口,該 Servlet 用於接受指定 URL 的 HTTP 請求,從該 Servlet 的 getDatabaserUrl 方法中我們可以看見數據源的來源,注意代碼中的 data 目錄,這個就是示例數據的數據來源,我們打開該目錄下的 exampledb.script 文件就發現該數據庫包含上文提及的 4 張表。因此我們知道 exampledb 有四張表也就是有四種 REST 資源,sqlRest 會通過映射將這四種資源暴露為 REST 風格的 Web 服務。我們也會發現,數據庫中每個表必須有一個 primary key 的定義,這就是資源的唯一標識符。這裡我們就了解到 sqlRest 的數據庫配置,通過這樣簡單的配置和 sqlrest 的 Servlet,hpersonic 數據庫中的數據就被映射成為 Web 上的具有唯一 URL 的 Rest 資源。請特別注意 Item 表的定義,它定義了 2 個字段的組合作為 primary key,我們可以通過 http://localhost:8080/sqlrest/ITEM/1 來查看 INVOICEID = 1 的 Invoice 中的所有 Item:
圖 6. INVOICEID 為 1 的 Invoice 中的所有 Item 資源
由於 sqlRest 目前版本的尚不是很完善,雖然數據庫的定義中主鍵可以包含多個字段,但是在解析參數的時候,只會將參數關聯到主鍵中第一個字段的值,例如即使我們使用 http://localhost:8080/sqlrest/ITEM/1/0 希望能夠返回 id 為 1 的 incovice 中 id 為 0 的 Item,但是結果仍然和 圖 6 相同。因為 sqlRest 忽略了後面的參數。如果需要,您可以通過修改 sqlRest 源碼,使之支持這樣的 URL 來通過多參數更為精確的定位數據庫中的資源。
在我們的 Web 2.0 實踐中,我們使用了形如 http://localhost:8080/sqlrest/INVOICE1/ITEM/0 這樣的 URL 來能夠更好的表達我們的意思,但是 REST 並沒有強制要求 URL 的定義和參數的定義形式,REST 不是規范,也不是標准,只是一種設計風格。在我們的實踐中我們覺得這樣的 URL 能夠更好的表達資源的含義,也有利於服務器端的解析。
Rest 服務的測試
到目前為止,我們只對 sqlRest 的 Rest 服務進行了基於浏覽器的 GET 資源的測試,我們知道一個 Rest 服務將會實現 GET,DELETE,UPDATE,CREATE 四種類型的接口,我們如何來測試這些接口呢?我們又如何確定 sqlRest 能自動的將這些接口和對數據庫的操作 (CRUD) 映射起來了呢 ? 視具體項目的情況,我們有多種的選擇。
最簡單的 Java 測試代碼
最簡單的情況下,我們只需要使用 JDK 提供的 HTTP 相關的 API 來編寫 Java 代碼作為 Rest 客戶端,此時您無需額外的工具或者 jar 包,這主要會使用到 URL 和 HttpURLConnection 類。讀者可以參考清單 2 中的示例代碼。
清單 2:使用簡單的 Java 代碼測試 REST 服務
URL url= new URL( “ http://localhost:8080/sqlrest ” );
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setRequestMethod("GET");
con.connect();
InputStream in= con.getInputStream();
byte[] b= new byte[1024];
int result= in.read(b);
while( result!=-1){
System.out.write(b,0,result);
result= in.read(b);
}
sqlRest 的源碼中提供了一個 RestClient 類 ( 位於 rest.client 包 ),該類是一個封裝了很好的 REST 服務 Java 客戶端,我們只要稍作修改就能將該 REST 服務的輸出返回到字符串,而不是僅僅打印在控制台。
Poster,無需寫代碼的測試
如果不願意編寫測試代碼,我們也可以使用一些工具來幫助測試 REST 服務。 Poster 就是這樣一個工具,它是一個 Firefox 插件,我們可以從以下的 URL 獲取 Poster 的安裝下載:https://addons.mozilla.org/zh-CN/firefox/addon/2691 。安裝後重新啟動 Firefox,從“ View - >SideBar - >Poster ”菜單啟動該插件,我們就可以看見如圖 7 的界面,該工具簡單易用,我們可以輕松的發送出 REST 測試報文並觀察其輸出。
圖 7. Firefox Poster 使用示例
通過使用 FireFox Poster 插件,您可以發出各種各樣的 HTTP 請求,從而達到不編寫代碼測試 REST 服務的目的。
REST 服務的自動化測試方法
實際項目中,我們總是希望有一些自動化的方法, sqlRest 的源碼中已經提供了基於 JUnit 和 Apache Common HTTP 的自動化測試代碼,讀者可以參考源碼包中的 MainServletTest 類。這是一個 JUnit 測試用例,覆蓋了對 sqlRest 服務的各種測試,通過運行該測試用例,我們就實現了對 sqlRest 服務的全面測試。該測試用例使用了 Apache Common HTTP 包和 JUnit 包一起,來自動化的測試 REST 服務。在讀者使用其它 RESTRest 框架時,這種測試方法也是有效的。我們也可以在項目中使用 ANT 的腳本來自動的運行 JUnit 的測試,這樣就獲得了更為全面的自動化的測試能力,使用 ANT 這樣的腳本工具,您可以獲取從編譯到部署再到測試的全方位的自動化能力。讀者可以參考 MainServletTest 中的代碼構建基於 JUnit 和 Apach Common HTTP 的自動化測試。
AJAX 測試方法
顯然 REST Service 並不是全部要面向 Java 客戶端的,實際的項目中,很多的 Web 2.0 的解決方案後台會使用 REST 服務來提供資源,我們完全可以定義一個簡單的 AJAX 客戶端(任何客戶端本來就具有對服務器端測試的潛在能力)來進行 Rest 服務的測試,雖然最終的效果看起來也許和 Poster 差不多,但是我們的 AJAX 測試是具有業務邏輯的測試,也是一種高級的測試形態。一個典型的例子是我們會在頁面中使用 AJAX 方法獲取遠程的 REST 資源,然後展現給用戶。
配置基於 DB2 的 sqlRest 服務
准備數據庫和配置文件
筆者使用的是 DB2 8.2 版本。我們將使用 DB2 數據庫的 sample 數據庫,如果您 DB2 沒有配置,您可以通過在 DB2 的命令行中運行 DB2sampl 命令來安裝示例數據庫, 為簡化配置,我們使用本機數據庫(DB2 和 Tomcat 在一台機器上), 我們的示例將演示如何將 Sample 數據庫中的 EMPLOYEE 表中的數據配置為 REST 服務的資源。
找到 sqlrestconf.xml 將內容修改為如清單 3:
清單 3:sqlRest 連接 DB2 的配置文件
<database>
<jdbc-driver-class>com.ibm.db2.jcc.DB2Driver</jdbc-driver-class>
<database-url>jdbc:db2:sample</database-url>
<user>user</user>
<password>pwd123</password>
</database>
這裡的用戶名和密碼請使用讀者自己的 DB2 用戶名和密碼配置。請到 DB 的安裝目錄下找到 jdbc 驅動程序,例如筆者的目錄: D:\Program Files\IBM\SQLLIB\java,拷貝 db2jcc.jar 和 db2jcc_license_cu.jar 到 sqlRest 項目的 WEB - INF 的 lib 目錄下,並重新部署。
啟動和測試
當配置文件和 DB2 數據庫客戶端准備完成後,重新部署後我們可以在 Tomcat 控制台中看見 “ Wrong table name ”的錯誤,啟動失敗,是什麼原因呢?這就因為目前 sqlRest 還在開發過程中,有些功能的發展還不太完善,我們已經在前文提到過,sqlRest 必須要一個主鍵來表示表中的數據資源,而我們的 Sample 數據庫中很多的表沒有主鍵或者主鍵不滿足條件。只有數據庫中的表滿足 sqlRest 的要求,引擎才能通過 URL 找到唯一的資源,因此為了示例的方便,我們需要對 Sample 數據庫做一些調整:
刪除您的 Schema 下無用的表:僅保留 EMPLOYEE 表;
給 EMPLOYEE 表增加主鍵(EMPNO);
修改後的表如圖 8 所示:
圖 8. 增加了主鍵的 EMPLOYEE 表
此時部署運行,一切正常,在浏覽器中 http://localhost:8080/sqlrest 我們將看見系統 EMPLOYEE 表作為 REST 資源暴露出來。 通過這個簡單的例子我們認識到 salRest 可以很容易的配置為支持各種數據庫,讀者也可以嘗試用上文類似的 URL 來測試該 EMPLOYEE REST 服務,您可以充分的感受到 sqlRest 的方便和快捷。也許您會問,難道唯一的 URL 只能要求數據庫的唯一主鍵嗎?當然不是這樣的,這取決於服務器端框架對 URL 的約定,我們完全可以使用更復雜的 URL 來表示資源,但是需要服務器端支持對更復雜 URL 的解析功能和對多字段主鍵的支持。我們相信 sqlRest 將來的版本將提供對數據庫多鍵的支持,但是現在我們只能使用單一字段作為 URL 中的主鍵。讀者如果需要,可以對 sqlRest 的源碼進行一定的修改來支持更為復雜的 URL 格式,這樣的修改不是很麻煩。
也許有讀者會問,修改現有的表,使其滿足 sqlRest 的要求,還算可以容忍,但是刪除數據庫中其它不滿足條件的表就不對了,如果有的是系統表怎麼辦?在我們的例子中,我們使用 DB2 的示例表主要是從簡單的角度出發,實際項目中,可能有兩種情況:
1 完全重新設計數據庫 : 此時只要滿足 sqlRest 主鍵的要求就沒有問題,
2 基於現有數據庫的應用 : 此時就需要對現有數據庫進行改造,但這顯然會影響已有的應用,因此我們不推薦(上文我們對 Sample 數據庫的修改只是作為示例)。
對於暫時沒有成為 REST 資源的表,又不想改造數據,我們可以通過一個簡單的方法來實現。我們的做法是直接修改 sqlRest 的源碼,這個修改非常簡單,我們只要修改 DatabaseAnalyser 類的 getDatabaseInfo 方法,使得 sqlRest 在解析數據庫表的時候不要拋出異常,遇見不滿足條件的表就自動跳過就可以,這樣的修改不影響已有的滿足條件的表。
目前 sqlRest 還有一個限制就是每次配置只能使用一個數據庫,這時候因為 URL 的契約中並沒有使用數據庫的名字作為名字空間,同樣的道理,通過對源碼的學習我們也意識到修改源碼實現對多數據庫的支持並不是很困難的事情。使用開源軟件的好處之一就在於您可以根據自己的需要對它進行修改。
結束語
通過本文我們可以了解到 sqlRest 的設計思想,就是將數據庫資源直接暴露為 REST 服務,這樣可以大量的節省我們的重復編碼,提高 We2.0 時代處理數據庫資源的快捷,同時我們也看 sqlRest 的一些不足和有待發展的地方。我們注意到在互聯網世界中,存在一些大量使用數據庫資源的應用,使用 sqlRest 這種將數據庫資源直接暴露為 REST 服務的方式可以極大的提高效率,降低開發和後台維護的成本。對於這樣一類應用,sqlRest 這種模式無疑是有很大的用武之地的。因此我們可以預見將來的數據庫很可能會內置對 REST 服務的支持,就像對現在的一些數據庫會內置對 SOAP Web 服務的支持一樣。
聲明:本文僅代表作者的個人觀點,不代表 IBM 的立場。
本文配套源碼