REST service 化一个数据系统(REST Service 的最佳实践,第 2 部分)
引言
在 Web 變成可編程的 platform 的進程中,有一些應運而生的新的服務的應用場景。我們來看一個具體的例子。Lily 是 Web 2.0 team 一個 Web 開發人員,她想買一些 Web 2.0 開發相關的書來開闊和加深她對 Web 2.0 技術的理解。她希望怎么做呢?她打開她訂閱的 eBay暢銷書服務(一個 Feed),看到目前最暢銷的一組關于 Web 2.0 的書籍,在她瀏覽這些書的介紹的時候,她還想看看這些書在 Amazon上的書評。等她決定好了買哪些書的時候,她想最好能貨比三家,她要知道各個書商提供的 書的價格比較,她選擇了一家性價比比較合理的書商后需要付錢購買,比如用 Goolge Check Out 來付賬。服務之間的導航關系如圖 1 所示。
圖 1. 買書所需用的的服務以及關系
今天,lily 如果想做到這一點,要么 IT developer 幫她專門開發一個集成的系統,把這些不同 vendor 提供的 Web 服務集成起來:eBay 提供的 暢銷書服務,Amazon 提供的 書評服務,directtextbook.com 提供的 書的價格比較 服務,Google 提供的 Goolge Check Out 服務。但顯然這種方法實施性和適應性都較差,因為 lily 的需求不固定,瀏覽的路徑也不固定,IT developer 很難決定需要集成哪些 Web 服務,很難滿足像 lily 這樣的不固定的需求。另外,Lily 還可以選擇使用現有的 Mashups 的產品,如 IBM Mashup Center,但是她需要做比較枯燥乏味而又不是那么容易的 widget 之間的 wiring,而且她還要自己知道這些個相關的 Web 服務。對 lily 而言,如果有這樣一個系統,她可以從最開始的 eBay 暢銷書服務開始瀏覽,然后查看 Amazon 的書評,就像現在的 Web 上面的 HTML 頁面之間通過 hyperlink 瀏覽一樣,她也可以很容易的通過鼠標的點擊、選擇、輸入一些很少的東西再輔以搜索就可以從一個服務達到另一個服務,平滑、自然、簡單、輕松。
這是一個美好的夢想,如何能實現這個夢想?作為第一步,每個企業或者業務系統都必須服務化,實現數據的靈活訪問,也就是企業信息系統的解鎖,讓普通人以 Web 的方式就能輕松的訪問。基于此,我們分析 lily 買書這個場景中的幾個 Web 服務可知,這些 Web 服務雖然來自不同的系統,但他們之間是有聯系的。eBay 提供的暢銷書服務的數據里面包含書的 ISBN 信息,而 Amazon 的書評服務和 directtext.com 提供的書的價格比較服務都需要 ISBN 信息才能返回相應的書的書評和價格比較。另外如果現在很多人都用 Goolge Check Out 付賬,說明他的 popularity 比較高。當用戶想要購買的時候,可以用它。如果我們要滿足 lily 的要求而又不需要 IT developer 的參與,我們就需要一種方式描述和建立服務之間的關聯,發現和利用這種關聯來改善普通人們使用互聯網的體驗 – 像使用 HTML 為基礎的內容 Web 一樣來自由地從一個服務瀏覽到另外一個服務,這正是 REST 架構風格可以解決的問題。
傳統的 Web 業務系統的分析
在軟件體系架構設計中,分層式結構是最常見,也是最重要的一種結構。Martin Fowler 在《 Patterns of Enterprise Application Architecture 》一書中,將整個架構分為三個主要的層:表示層、領域層和數據源層,如圖 2 所示。
表示層 (User Interface Layer) 位于最上層,離用戶最近,為用戶提供一種交互式操作的界面,用于顯示數據和接收用戶輸入的數據。
業務邏輯層(Business Logic Layer)是系統架構中體現核心價值的部分。它的關注點主要集中在業務規則的制定、業務流程的實現等與業務需求有關的系統設計,也即是說它是與系統所應對的領域(Domain)邏輯有關,很多時候,也將業務邏輯層稱為領域層。業務邏輯層在體系架構中的位置很關鍵,它處于數據訪問層與表示層中間,起到了數據交換中承上啟下的作用。
數據訪問層(Data Access Layer)有時候也稱為是持久層,其功能主要是負責數據庫的訪問,直接操作數據庫,針對數據的增、刪、改、查。簡單的說法就是實現對數據表的 Select,Insert,Update,Delete 的操作。如果要加入 ORM 的元素,那么就會包括對象和數據表之間的 mapping,以及對象實體的持久化。?
分層的結構給 Web 應用的開發帶來了很多好處,比如開發人員可以只關注整個結構中的其中某一層;可以很容易的用新的實現來替換原有層次的實現;可以降低層與層之間的依賴;有利于標準化;利于各層邏輯的復用。現在也是作為 Web 應用的主流架構提供。
圖 2. Web 應用的三層架構
企業信息系統面臨的挑戰
盡管企業現在有很多的數據系統和應用系統,但是還仍然面臨以下的嚴峻挑戰:
以更靈活的 Mashup 的視角看業務系統
Mashup 和現有的 Web 應用系統
Mashup 是 Web 2.0 領域里面一個特別火的詞,wikipedia 上的解釋是“網絡聚合應用,由一個或者多個信息源整合起來的網站或者網絡應用”。從企業的角度看 Mashup,應該理解成更“靈活的數據的使用和更簡單的應用的構建”。圖 3 是一個“客戶 360 度信息”的 Mashup。可以看出,在這個 Mashup 中包含五個服務,分別是:①以表格形式展示的客戶的基本信息;②以曲線形式展示的沃爾瑪的股票信息;③以時間線形式展示的沃爾瑪的購買行為;④以柱狀圖形式展示的客戶季度收入情況;⑤以 feed 閱讀器形式展示的沃爾瑪的新聞。可以看出,這個 Mashup 里面包含的服務來自好幾個數據源:①客戶的基本信息來自企業的 CRM 系統;②股票信息來自 google 財經;③客戶的購買行為來自企業的采購系統;④客戶季度銷售額來自 google 財經;⑤新聞來自 google news。
圖 3. 一個“客戶 360 度信息”的 Mashup
從上面的例子我們可以看出 Mashup 和現有的 Web 應用系統相比的優勢:
Mashup 和傳統系統集成技術
Mashup 是 Web 2.0 領域里面一個特別火的詞,wikipedia 上的解釋是“網絡聚合應用,有一個或者多個信息源整合起來的網站或者網絡應用”。從企業的角度看 Mashup,應該理解成“更靈活的數據的使用和更簡單的應用的構建”。那很多人要問了:從這個角度講,Mashup 和傳統的 BPM、BI、EII、ESB 類似的集成技術有啥不一樣呢?我們來分別看一看。
相對于這些傳統的企業集成技術,Mashup 是一種擴展和補充。Mashup 提供更靈活的數據使用和展示,主要關注情景式的、瞬態的應用,就像在引言部分 lily 買書的例子一樣。傳統的這些集成系統也可以以 Web 服務的形式為 Mashup 提供強大的企業數據源。
Mashup 的解決方案
前面兩個小節分析了 Mashup 和現有 Web 應用系統以及傳統信息集成集成的優缺點,這節主要講述以 Mashup 技術為基礎的解決方案。在業務人員能夠創建 Mashup 應用之前,需要把信息和服務發布成為可以 Mashup 的格式,通常而言就是 Feeds 或者 Widgets。
圖 4. 基于 Mashup 的解決方案概念圖
最佳實踐— REST 服務化現有系統
RESTify 數據層
這一節主要講述識別、創建和發布數據服務的方法。
識別數據服務
識別數據服務是最關鍵的一步,主要解決針對一個數據系統,應該提供哪些數據服務。根據該系列的第一篇文章“REST Service 的最佳實踐 第一部分:重新解析 REST Service”,讀者已經知道,RESTful Web 服務的核心是以“資源”為中心,而這里實體 - 關系圖中的“實體”和“資源”在語義上有很大的關聯性,所以這里提供一個基于 E-R(Entity-Relationship)模型的識別數據服務的方法論。實體 - 關系圖是一個在數據庫設計時幫助架構師進行思考的重要的概念圖,反映出信息系統的實體以及實體和實體間的關系,因此實體 - 關系圖一個很好的手段去發現曝露出去的資源。圖 5 是一個在線購物網站的 E-R 圖,我們將以此為例,講述識別服務的方法。
圖 5. 一個在線購物網站的實體 - 關系圖
識別數據服務的步驟如下:
如圖 5 中所以,黃色的方框表示的是“實體”。本質上,這些“實體”對應的是系統的“資源”,可以看出,一個在線購物網站,需要提供的資源包括:買家、賣家、供貨商、商品、訂單、賬單、快遞單、購物車、買家評價、分類。
實體 - 關系圖中定義的“實體”之間的“關系”為“一對一”、“一對多”、“多對多”的關系。把實體 - 關系圖中的“關系”發布成數據服務,這里所說的“關系”不是實體之間的數量對應關系,而是語義上數據依賴、相似關系。實體之間的語義關系有幾種,如圖 6 所示。
圖 6. 實體之間的語義關系圖
下面分別來闡述圖 6 所示的關系對應的數據服務。
相同屬性
“相同屬性”指的是“實體 A 和 B 有相同屬性”。在“在線購物”的這個場景中,由“相同屬性”找出來的數據服務可能包括:具有相同“收獲地址”的訂單;具有相同“風格”的商品,具有相同屬性“打折商品”的商品,等等。一個應用場景是:當用戶瀏覽一件 T 恤的時候,假如“T 恤”這個實體的屬性有:風格、面料、尺寸、品牌這四個屬性,我們可以識別出來跟實體“T 恤”相同屬性的數據服務有兩類:一類是相同實體類型的,如相同“風格”的“T 恤”,相同“面料”的“T 恤,相同“尺寸”的“T 恤”,相同“面料”的“T 恤”;還有不同實體的具有“相同屬性”的數據服務包括:相同“風格”的“褲子”,相同“面料”的“褲子”,相同“尺寸”的“褲子”,相同“面料”的“褲子”等等。
從技術來講,“相同屬性”這種識別出來的數據服務其實就是給在 1)中識別出來的實體創建了很多不同的查詢條件。通過“屬性”,把數據服務關聯了起來。通過本系統的第一篇文章,讀者已經知道了 REST 的一個核心思想是創建相互聯系的服務,而“相同屬性”識別數據服務的方法,一方面我們可以得到很多數據服務,另一方面,這些數據服務天然的就相互聯系在一塊,和 REST 的核心思想一致。
從用戶的角度來講,“相同屬性”是一種導航的線索,通過“相同屬性”的關系識別出來的數據服務,使得人們獲得一種靈活查詢數據的能力。
操作
“操作”關系是指“實體 A 對實體 B 做了什么操作”。比如在“在線購物”的這個場景中,“買家”是一種類型的實體,“商品”是另一種類型的實體,“買家 A ‘購買’了商品 B”就是“操作”關系的一種實例。
通過“操作”關系,可以定義的數據服務包括:買家 A 購買的商品列表,買家 A 瀏覽的商品列表,買家 A 評價的商品列表等等。
和通過“相同屬性”識別的數據服務類似,通過“操作”關系識別出來的數據服務也天然的符合 REST 的約束。
實體 A 的實例和實體 B 有相同的關系
“實體 A 的實例和實體 B 有相同的關系”指的是同種類型的實體的不同實例和第二種類型的實體有相同的關系。還是拿“在線購物”為例,買家 A“購買”了商品 C,買家 B 也“購買”了商品 C,那么可以提供一個數據服務為“同時購買商品 C”的買家列表。細心的讀者已經發現,這種關系和“相同屬性”的關系有相似之處,不同點在于“相同屬性”的關系不依賴于第二種實體,而這種關系依賴于外來的實體,識別數據服務的規則是一樣。
和前兩種通過“關系”識別出來的數據服務一樣,通過“實體 A 的實例和實體 B 有相同的關系”的關系識別出來的數據服務也天然的符合 REST 的約束。
通過前兩種通過“實體”和“關系”的方法,我們已經得到了很多 RESTful 的數據服務,但這并沒有把所有的數據服務都找到,還有一類隱含的數據服務。在“在線購物”的場景中,“買家”(一種實體 - 關系圖中的實體)可以對“商品” (一種實體 - 關系圖中的實體)進行“評價”(一種實體 - 關系圖中的關系),過程中產生了一個隱含的實體“評價內容”。
也就是說,在實體 - 關系圖中的“關系”,除了像 CRUD(創建、查看、更新、刪除)這些直接作用在另一個實體的關系以外,還有像“評價”、“打分”等關系,這些關系會產生一些新的實體。我們需要分析實體 - 關系圖中的關系,識別出這些隱含的數據服務。
創建和發布數據服務
現在有很多平臺可以用來創建數據服務,IBM 就提供了幾個選擇,例如 IBM WebSphere sMash, Web 2.0 Featurepack for WAS 和 Infosphere Mashuphub 等。利用這些平臺,開發人員可以很容易的利用腳本語言或者 Java,或者通過 Mashuphub 的 plugin 來創建這些數據服務。下面以 Mashuphub 為例,來簡要的說明創建數據服務的方法。
首先假設“在線購物”的數據庫 schema 設計(部分)如清單 1 所示:
清單 1.“在線購物”的數據庫 schema 設計(部分)
| 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | create table Buyers( ?????id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, ?????name varchar(64) NOT NULL, ?????gender varchar(10), ?????salary INTEGER, ?????email varchar(50), ?????phonenumber varchar(50), ?????location varchar(256),?? ?????starlevel double NOT NULL default 0,?? ?????PRIMARY KEY (id) ???); create table Sellers( ?????id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, ?????name varchar(64) NOT NULL, ?????phonenumber varchar(50), ?????location varchar(256),?? ?????starlevel double NOT NULL default 0,?? ?????PRIMARY KEY (id) ???); create table Delivery( ?????id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, ?????deliverto varchar(256) NOT NULL, ?????consignee varchar(50), ?????consignee_phonenumber varchar(50), ?????delivery_company varchar(256), ?????PRIMARY KEY (id) ???); create table Orders( ?????id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, ?????buyerid INTEGER NOT NULL, ?????sellerid INTEGER NOT NULL, ?????deliveryid INTEGER NOT NULL, ?????generatetimestamp INTEGER default 0, ?????confirmtimestamp INTEGER default 0, ?????paymenttimestamp INTEGER default 0, ?????payment double NOT NULL default 0,??????? ?????PRIMARY KEY (id), ?????FOREIGN KEY (buyerid) REFERENCES Buyers(id), ?????FOREIGN KEY (sellerid) REFERENCES Sellers(id), ?????FOREIGN KEY (deliveryid) REFERENCES Delivery(id), ???); create table Books( ?????id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, ?????title varchar(128) NOT NULL, ?????author varchar(128), ?????manufactor varchar(128), ?????averageRating? double, ?????price double, ?????PRIMARY KEY (id) ???); create table TShirts( ?????id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, ?????name varchar(64) NOT NULL, ?????color varchar(64), ?????tsize varchar(10), ?????brand? varchar(64), ?????fabric varchar(64), ?????style varchar(64), ?????PRIMARY KEY (id) ???); create table Trousers( ?????id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, ?????name varchar(64) NOT NULL, ?????color varchar(64), ?????tsize varchar(10), ?????brand? varchar(64), ?????fabric varchar(64), ?????style varchar(64), ?????PRIMARY KEY (id) ???); create table Scarves( ?????id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, ?????name varchar(64) NOT NULL, ?????color varchar(64), ?????tsize varchar(10), ?????brand? varchar(64), ?????fabric varchar(64), ?????style varchar(64), ?????PRIMARY KEY (id) ???); create table Shoes( ?????id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY, ?????name varchar(64) NOT NULL, ?????color varchar(64), ?????tsize varchar(10), ?????brand? varchar(64), ?????fabric varchar(64), ?????style varchar(64), ?????PRIMARY KEY (id) ???); |
接下來用 IBM Infosphere Mashuphub 來把 Database 里面的數據服務化。Mashuphub 可以把各種各樣的企業數據源發布成數據種子(Feed),詳細的介紹請參考 Mashuphub info center。這里簡單的介紹下用 Mashuphub 把數據庫的數據發布成種子。有以下幾步:
圖 7. 選擇數據源
圖 8. 配置 JDBC 數據源
圖 9. 用 SQL 生成器生成 SQL
圖 10. 提供一些描述性的信息
圖 11. 發布成功以后
RESTify 展示層
表示層主要負責數據的展示,我們稱之為 viewer。開發 Viewer 需要具有較多的 Web 開發的技巧,例如 HTML,JavaScript 和 CSS 等。為了重用數據的展示,和數據層一樣,我們也需要模塊化,REST 服務化。現在流行的可重用的 Web 小組件有很多的規范,像 google gadget,IBM iWidget 等等。下面以 iWidget 規范為例,來展示一個 viewer 的開發過程。下面一個 widget 為例,簡要的介紹,結合數據服務,widget 的開發過程。
下面以一個簡單的用來顯示亞馬遜圖書搜索的數據服務的 viewer。圖書搜索數據服務提供的數據如清單 2 所示:
清單 2. 圖書搜索服務的數據樣本
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <entry xmlns:aws="http://webservices.amazon.com/AWSECommerceService/2005-10-05"> <title> Don't Make Me Think: A Common Sense Approach to WebUsability </title> <aws:ASIN> 0321344758 </aws:ASIN> <link rel="alternate" href="http://www.amazon.com/Dont-Make-Think-Usability-ebook/dp /B000SEGQNS%3FSubscriptionId%3DAKIAJ3RS7ICEOBT6PH4Q%26tag%3Dws%26linkCode%3Dxm2%26cam p%3D2025%26creative%3D165953%26creativeASIN%3D0321344758"/> <icon> http://ecx.images-amazon.com/images/I/51GRhbtsUQL._SL160_.jpg </icon> <logo> http://ecx.images-amazon.com/images/I/51GRhbtsUQL._SL160_.jpg </logo> <content type="application/xml"> <p:row xmlns:p="http://www.example.com"> <aws:title> Don't Make Me Think: A Common Sense Approach to WebUsability </aws:title> <aws:ASIN> 0321344758 </aws:ASIN> <link rel="alternate" href="http://www.amazon.com/Dont-Make-Think-Usability-ebook /dp/B000SEGQNS%3FSubscriptionId%3DAKIAJ3RS7ICEOBT6PH4Q%26tag%3Dws%26linkCode%3Dxm2%26 camp%3D2025%26creative%3D165953%26creativeASIN%3D0321344758"/> <author> Steve Krug</author> <aws:Manufacturer> New Riders Press </aws:Manufacturer> <aws:lowestNewPrice> $23.30 </aws:lowestNewPrice/> <aws:averageRating> 4.5 </aws:averageRating> <p:image> http://ecx.images-mazon.com/images/I/51GRhbtsUQL._SL160_.jpg </p:image> </p:row> </content> </entry> |
按照 iWidget 的規范,開發一個 widget 需要提供 widget 的定義文件,如清單 3 所示:
清單 3. 圖書搜索服務 viewer 的 widget 定義文件
| 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | <iw:iwidget name="amazonSearchViewer" xmlns:iw="http://www.ibm.com/xmlns/prod/iWidget" ?iScope="common.iwidget.amazonSearchViewer" allowInstanceContent="true" ??supportedModes="view edit" mode="view" lang="en"> <iw:itemSet id="attributes" > <iw:item id="feedURL" value="" readOnly="false"/> <iw:itemDescription name="feedURL" type="url.feed.tabular" description="Customized widget for displaying book list from Amazon"/> <iw:item id="title" value="" readOnly="false"/> <iw:item id="view" value="table" readOnly="false"/> <iw:item id="titleElement" value="title" /> <iw:itemDescription name="titleElement" type="text" description="book title element"/> <iw:item id="authorElement" value="author" /> <iw:itemDescription name="authorElement" type="text" description="book author element"/> <iw:item id="publisherElement" value="Manufacturer" /> <iw:itemDescription name="publisherElement" type="text" ?description="book publisher element"/> <iw:item id="asinElement" value="asin" /> <iw:itemDescription name="asinElement" type="text" description="book isbn element"/> <iw:item id="ratingElement" value="AverageRating" /> <iw:itemDescription name="ratingElement" type="text" description="book rating element"/> <iw:item id="imageElement" value="image" /> <iw:itemDescription name="imageElement" type="text" description="book rating element"/> <iw:item id="priceElement" value="lowestNewPrice" /> <iw:itemDescription name="priceElement" type="text" description="book price element"/> </iw:itemSet> <iw:resource uri="../styles/common.css" /> <iw:resource uri="amazonSearchViewer.js" /> <iw:content mode="view"> <![CDATA[ <div id="_IWID_serviceNode"> <div id="_IWID_loadingNode" ?style="display:none;margin-left:48%;margin-top:40px;height:80px;"> </div> </div> ]]> </iw:content> <iw:content mode="edit"> <![CDATA[ ????]]> </iw:content> </iw:iwidget> |
itemSet 用來描述 widget 的可配置信息,包括 FeedURL,widget 的標題,還包括和數據服務相關的數據項的描述:titileElement 用來描述圖書的標題信息、authorElement 用來描述圖書的作者信息等等。
有幾種方式來定義 widget,清單 3 給出了其中的一種,用來展示一定類型的數據:圖書標題,圖書作者,圖書標號,圖書最新最低價格等等。這種方式編寫的 viewer 具有一定的普適性和可重用性,只要數據服務提供了這些信息都可以用這個 viewer 來展示。清單 4 給出了另一種 widget 的定義方式。
清單 4. 圖書搜索服務 viewer 的 widget 定義文件 2
| 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 29 30 | <iw:iwidget name="amazonSearchViewer" xmlns:iw="http://www.ibm.com/xmlns/prod/iWidget" ?iScope="common.iwidget.amazonSearchViewer" allowInstanceContent="true" ?supportedModes="view edit" mode="view" lang="en"> <iw:itemSet id="attributes" > <iw:item id="feedURL" value="" readOnly="false"/> <iw:itemDescription name="feedURL" type="url.feed.tabular" description="Customized widget for displaying book list from Amazon"/> <iw:item id="title" value="" readOnly="false"/> <iw:item id="view" value="table" readOnly="false"/> </iw:itemSet> <iw:resource uri="../styles/common.css" /> <iw:resource uri="amazonSearchViewer.js" /> <iw:content mode="view"> <![CDATA[ <div id="_IWID_serviceNode"> <div id="_IWID_loadingNode" style="display:none;margin-left:48%;margin-top:40px;height:80px;"> </div> </div> ]]> </iw:content> <iw:content mode="edit"> <![CDATA[ ????]]> </iw:content> </iw:iwidget> |
清單 4 的 itemSet 只描述了數據源的 FeedURL,而沒有更多的關于可配置數據項的描述,這種方式定義的 widget 把對數據的處理隱含在代碼里面,局限性比較大,可配置性差,可重用性也差。下面需要寫一些 javascript 來處理一個展示的邏輯,如清單 5 所示。負責創建頁面元素,并發送 HTTP 請求取回來 feed 的結果。
清單 5. widget 的邏輯代碼
| 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | dojo.provide("common.iwidget.amazonSearchViewer"); dojo.declare("common.iwidget.amazonSearchViewer", null, { title:null, serviceURL:null, view:null, onLoad: function(){ this.domID = "_" + this.iContext.widgetId + "_"; var att = this.iContext.getiWidgetAttributes(); ???this.title = att.getItemValue("title"); ???this.serviceURL = att.getItemValue("feedURL"); ???this.view = att.getItemValue("view"); ???? ???var loading = dojo.byId(this.domID + "loadingNode"); ???var innerNode = document.createElement("div"); ???loading.appendChild(innerNode); ???new hyperservice.iwidget.ui.Loading({},innerNode); }, onView: function(){ ???var serviceNode = dojo.byId(this.domID + "serviceNode"); ???var innerNode = document.createElement("div") ???serviceNode.appendChild(innerNode) ???? ??????//fetch the feed, ???var self = this; ???var loadCallbackFunc = function(feed){ ???new iwidget.ui.FeedListViewer ???({feed:feed,viewTitle:feed.title,serviceInstance:self.serviceURL, ???selectedItemViewer:"common.ui.AmazonSearchListViewerItem"},innerNode); ???} ???? ???var errorCallbackFunc = function(data){ ???console.error("failed to fetch a feed with url:"+self.serviceURL); ???console.error(data) ???} ???feedFetcher.fetch(this.serviceURL, loadCallbackFunc,errorCallbackFunc); ???} ???} }); |
在清單 5 中創建 FeedListViewer 的時候有一個參數是 selectedItemViewer,用來設置具體的關于數據的展示內容部分,具體的實現代碼如清單 6 所示。
清單 6. 關于數據的展示部分的代碼
| 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | dojo.provide("common.ui.AmazonSearchListViewerItem") dojo.declare("common.ui.AmazonSearchListViewerItem", [dijit._Widget, dijit._Templated],{ imgSrc:null, entry:null, serviceInstance:null, //dojoattachpoint imgNode:null, itemDetailNode:null, titleNode:null, authorNode:null, manufacturerNode:null, //priceMessageNode:null, priceNode:null, ASINnode:null, ratingNode:null, averageRatingNode:null, averageRatingNode:null, constructor:function(){ this.entry = null; }, postCreate:function(){ this.createLiveText(); }, createLiveText:function() { if(!this.entry){ return; } var foundPriceElement = false; this.imgNode.src = this.imgNode; var contextRow = {} dojo.forEach(this.entry.dataitemRows,function(dataitem){ //using xpath ??????????????????????????????????????????????????????// as dataitem identifier contextRow[dataitem.xpath] = dataitem.value; }) dojo.forEach(this.entry.dataitemRows,dojo.hitch(this,function(dataitem){ if(dataitem.name.indexOf("image")!=-1){ this.imgNode.src = dataitem.value }else if(dataitem.name.toLowerCase()=="title"){ this.titleNode.innerHTML = "<span>"+dataitem.value+"</span>" }else if(dataitem.name.toLowerCase()=="author"){ this.authorNode.innerHTML = "<span>"+dataitem.value+"</span>" }else if(dataitem.name.toLowerCase()=="manufacturer"){ this.manufacturerNode.innerHTML = "<span>"+dataitem.value+"</span>" }else if(dataitem.name.toLowerCase() == "lowestnewprice"){ foundPriceElement = true; if(dataitem.value!=""){ ????this.priceNode.innerHTML = "<span>"+dataitem.value+"</span>" ????this.priceNode.style.display = "inline" }else{ this.priceNode.style.display = "none" } }else if(dataitem.name.toLowerCase() == "asin"){ this.ASINnode.innerHTML = "<span>"+dataitem.value+"</span>" }else if(dataitem.name.toLowerCase() == "averagerating"){ var ratio=parseFloat(dataitem.value); if(Math.floor(ratio)==Math.ceil(ratio)) { dojo.addClass(this.ratingNode,"stars"); } var count=0-(5-Math.floor(ratio))*18; var style=count+"px"; dojo.style(this.ratingNode,{ backgroundPosition:style }); this.averageRatingNode.innerHTML="<span>("+dataitem.value+")</span>"; ?????if(!foundPriceElement){ //this.priceMessageNode.style.display = "none" this.priceNode.style.display = "none" } })) } }); .stars{ Background:transparent url(sample/imapges/star.png) no-repeat scroll 0 0; } |
具體的 viewer 的展示效果如圖 12 所示。
按照此種方法,開發人員可以快速的開發用來展示各種各樣類型數據的 widget。
結束語
Web 在轉化為一個可編程的平臺,越來越多的 Web Service 被發布出來,它們表現為 Feeds,REST APIs 和 Widgets。據 www.ProgrammableWeb.com 網站的統計,該網站已經擁有 1000 多個 Web API 而且以每天新增 2 個的速度在增加。來自 Google 的消息,Google gadget 現在已經有 45170? 多個。
本文通過深入的分析 Web 架構的業務系統所面臨的挑戰,即不夠靈活、可復用性差、業務人員難于參與到應用的構建等等,然后以 Mashup 的角度重新審視已有的業務系統,帶來一種全新的使用數據、業務邏輯、和展示層的思路,使得這些企業積累起來的資產能夠被更好的使用。然后,本文以“在線購物”應用為例介紹了以實體 - 關系模型為基礎的 REST 服務化已有的業務系統,包括識別、創建和發布“數據服務”、識別和創建“展示服務”。通過這些分析和方法的介紹,希望給需要提高系統的可重用性、靈活性、響應更快的開發人員提供一定的幫助。
總結
以上是生活随笔為你收集整理的REST service 化一个数据系统(REST Service 的最佳实践,第 2 部分)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 重新解析 REST Service(RE
- 下一篇: 把 SOAP 服务转化为 REST 服务