使用 XML 实现 REST 式的 SOA
什么是 SOA?
如果公司有大量應(yīng)用程序,這些程序供不同部門的承擔(dān)不同責(zé)任的職員使用,那么就適合使用面向服務(wù)體系結(jié)構(gòu)(Service Oriented Architecture,SOA)。這些應(yīng)用程序可以共享功能,但是功能的組合、用戶界面細(xì)節(jié)和易用性需求是不同的。與許多企業(yè)體系結(jié)構(gòu)一樣,SOA 也采用一個多層模型,但是它不只如此。在服務(wù)器中,功能分散在單獨的服務(wù)上。一個客戶機(jī)可以使用其中的一個或多個服務(wù),而一個服務(wù)也可以由許多客戶機(jī)使用。由此形成了一個松散耦合的體系結(jié)構(gòu),這大大提高了現(xiàn)有軟件的可重用性。
常用的重型實現(xiàn)
常用縮寫詞
- API:應(yīng)用程序編程接口(Application program interface)
- IT:信息技術(shù)(Information technology)
- XML:可擴(kuò)展標(biāo)記語言(Extensible Markup Language)
SOA 尤其適合大公司,大公司往往有數(shù)百個應(yīng)用程序,應(yīng)用程序之間缺少良好的集成,所以公司需要清理 IT 基礎(chǔ)結(jié)構(gòu)。SOA 是一種已經(jīng)證明有效的實踐,對于大型環(huán)境尤其有效。采用 SOA 的公司可以把遺留的應(yīng)用程序轉(zhuǎn)換為服務(wù),并把服務(wù)集成為現(xiàn)代應(yīng)用程序的后端。可以使用中間件技術(shù)對服務(wù)進(jìn)行組合,并對服務(wù)中的特定功能進(jìn)行訪問控制。因為在大型環(huán)境中對 SOA 的需求最為強(qiáng)烈,所以中間件技術(shù)的廠商通常把產(chǎn)品的重點放在大型和重型解決方案上。
SOA 和輕量型技術(shù)
SOA 背后的思想對于小公司同樣是有價值的。重型解決方案的設(shè)置成本和所需的人員技能可能使小公司不敢嘗試 SOA — 但這是不應(yīng)該的。暫且不要考慮重型實現(xiàn),我們先來考察一下 SOA 的基本概念:
- 從現(xiàn)有的或新的應(yīng)用程序中提取出服務(wù)
- 把服務(wù)集中起來,供許多客戶機(jī)使用
并沒有什么因素妨礙我們用輕量型技術(shù)實現(xiàn)這些思想。您可以先建立一個小型 SOA 并逐漸擴(kuò)展它。如果您的公司以后發(fā)展成大型跨國公司,隨時可以遷移到重型技術(shù)。
回頁首
什么是 REST?
SOA 通常是用 SOAP 協(xié)議實現(xiàn)的,服務(wù)由一個 WSDL(Web Services Description Language, Web 服務(wù)描述語言)文檔來描述。盡管有許多開發(fā)工具大大簡化了對 SOAP 和 WSDL 的處理過程,但是我仍然把它們看作重型技術(shù),因為如果不使用這些工具,SOAP 和 WSDL 是很難處理的。
也可以通過超文本傳輸協(xié)議(HTTP)發(fā)送簡單的消息來實現(xiàn) SOA。這基本上就是 REST 式 Web 服務(wù) (RESTful Web services) 的工作方式。Representational State Transfer(簡稱 REST,中文翻譯“具象狀態(tài)傳輸”。REST 這個名稱是由 Roy Fielding 首創(chuàng)的)并不是一個協(xié)議或技術(shù);它是一種體系結(jié)構(gòu)風(fēng)格。REST 是 SOAP 的輕量型替代品,它是面向資源的,而不是面向操作的。它常常被歸結(jié)為遠(yuǎn)程過程使用 HTTP 調(diào)用?GET、POST、PUT?和?DELETE?語句。我認(rèn)為,這只是第二個重要的步驟。
第一個(也是最重要的)步驟是把所有資源建模為 URL 形式。URL 容易記憶,同時能夠訪問無數(shù) Web 頁面。至少,如果建模方式適當(dāng)?shù)脑?#xff0c;很容易記住 URL(比如 http://www.ibm.com/developerworks/xml/)。如果過分重視?GET、POST、PUT?和?DELETE,就可能產(chǎn)生不容易記憶的 URL,比如 http://www.longfakeurl.com/pol_srdm/70612/9,3993.32?id=78688&lang=cz&st=idx。
在實踐中,使用 HTTP 的方法可以進(jìn)一步限制為?GET?和?POST?兩種方法,因為大多數(shù)瀏覽器對它們的支持很完善。可以對 http://domain.com/myresources/new 執(zhí)行?POST,以替代對 http://domain.com/myresources 執(zhí)行?PUT;對 http://domain.com/myresources/oldresource/delete 執(zhí)行?POST,以替代對 http://domain.com/myresources/oldresource 執(zhí)行DELETE。
REST 式的設(shè)計過程
在設(shè)計 REST 式 Web 服務(wù)時,可以采用以下四個步驟:
以下是具體的設(shè)計過程。假設(shè)您是一家航空公司的開發(fā)人員。公司有用于預(yù)訂航班的軟件,還有處理付款(現(xiàn)金和信用卡)的組件。它使用軟件跟蹤包裹、執(zhí)行內(nèi)部資源規(guī)劃和執(zhí)行許多其他任務(wù)。
假設(shè)機(jī)場登記處的職員使用一個客戶機(jī)應(yīng)用程序,這個程序訪問包裹跟蹤服務(wù),還使用一個服務(wù)為乘客分配座位。處理包裹的地勤人員只需要包裹跟蹤服務(wù),不需要其他服務(wù)。他們的客戶機(jī)只允許他們確認(rèn)已經(jīng)登記的包裹是否到達(dá)了。不允許他們登記新的包裹。
在這個示例中,我們將設(shè)計包裹跟蹤服務(wù)。首先,決定資源:旅行者、航班和包裹(注意,在出現(xiàn)?{id}?的任何地方,都可以填寫任意數(shù)字):
| http://luggagetracking.airlinecompany.com/bags/{id} http://luggagetracking.airlinecompany.com/flights/{id} http://luggagetracking.airlinecompany.com/travellers/{id} |
為每個資源選擇一種數(shù)據(jù)格式:
包裹:
| <bag id="{id}"> <traveller id="{traveller-id}"/> <flight id="{flight-id}" /> <status>{current-status: departure/plane/arrival}</status> </bag> |
航班:
| <flight id="{id}"> <travellers> <traveller id="{traveller-id-0}" /> <traveller id="{traveller-id-1}" /> <traveller id="{traveller-id-2}" /> </travellers> <bags> <bag id="{bag-id-0}" /> <bag id="{bag-id-1}" /> <bag id="{bag-id-2}" /> </bags> </flight> |
乘客:
| <traveller id="{id}"> <flight id="{flight-id}" /> <bags> <bag id="{bag-id-0}" /> <bag id="{bag-id-1}" /> <bag id="{bag-id-2}" /> </bags> </traveller> |
顯然,這個模型過于簡單了。對于當(dāng)前的示例,只需要支持兩個方法,因此這個模型已經(jīng)足夠了。登記處應(yīng)該能夠為乘客登記新包裹。在把包裹裝進(jìn)飛機(jī)時,地勤人員應(yīng)該能夠修改包裹的狀態(tài):
- 對 http://luggagetrackingairlinecompany.com/travellers/{id}/newbag 執(zhí)行?POST,返回一個?<bag>XML 結(jié)構(gòu)。
- 對 http://luggagetracking.airlinecompany.com/bags/{id}/status/{newstatus} 執(zhí)行?POST,返回修改后的 XML 結(jié)構(gòu)。
使用標(biāo)準(zhǔn)的 HTTP 狀態(tài)作為狀態(tài)碼。成功的操作都會返回 200。如果系統(tǒng)無法根據(jù)資源的 ID 找到它,就會返回 404。系統(tǒng)故障導(dǎo)致的任何錯誤都會返回 500。
代碼示例:URL 映射
可以使用多種方式把 URL 映射到實現(xiàn)方法。比較先進(jìn)的方法可能更靈活,應(yīng)該用在比較大的應(yīng)用程序中。這個小示例使用最簡單的方法:正則表達(dá)式。下面是 BagServlet 上的 post 方法示例,它把 URL 參數(shù)傳遞給底層 servlet。可以在本文的下載文件中找到完整的 servlet 代碼。注意,這里沒有實現(xiàn)實際的底層服務(wù)。 以下是該示例:
| protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Pattern pattern = Pattern.compile("^/?.*?/bags/(.*)/status/(.*)$"); Matcher matcher = pattern.matcher(request.getRequestURI()); if(matcher.matches()) { String bagId = matcher.group(1); String newStatus = matcher.group(2); bagService.changeBagStatus(bagId, newStatus); } } |
在調(diào)用這個 URL 時,如果成功,就會隱式地返回狀態(tài)碼 200。更有意義的是,代碼返回 XML 結(jié)構(gòu)。這個示例使用 XStream API 把 Java? 對象轉(zhuǎn)換成 XML 結(jié)構(gòu)。這個 API 需要的配置非常少,而且主要根據(jù)類中的字段名選擇元素名。
這個示例代碼使用下面這些簡單的類:
航班:
| package eu.adraandejonge.restfulsoa; public class Flight { String id; public Flight(String id) { super(); this.id = id; } } |
乘客:
| package eu.adraandejonge.restfulsoa; public class Traveller { private String id; public Traveller(String id) { super(); this.id = id; } } |
包裹:
| package eu.adraandejonge.restfulsoa; public class Bag { private String id; private Flight flight; private Traveller traveller; private String status; public Bag(String id, Flight flight, Traveller traveller, String status) { super(); this.id = id; this.flight = flight; this.traveller = traveller; this.status = status; } } |
假設(shè)底層的 BagService 返回一個包裹,包裹的航班 ID 是 1,乘客 ID 是 1,狀態(tài)是 new。請考慮下面的?GET?實現(xiàn):
| protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Pattern pattern = Pattern.compile("^/?.*?/bags/(.*)$"); Matcher matcher = pattern.matcher(request.getRequestURI()); if (matcher.matches()) { String bagId = matcher.group(1); Bag bag = bagService.retrieveBag(bagId); XStream xstream = new XStream(); xstream.alias("bag", Bag.class); xstream.alias("traveller", Traveller.class); xstream.alias("flight", Flight.class); xstream.useAttributeFor(Bag.class, "id"); xstream.useAttributeFor(Traveller.class, "id"); xstream.useAttributeFor(Flight.class, "id"); String xml = xstream.toXML(bag); response.getWriter().write(xml); } } |
在查詢這個 URL 時,它會返回以下信息:
| <bag id="1"> <flight id="1"/> <traveller id="1"/> <status>new</status> </bag> |
回頁首
還能做什么?
我選擇這些示例代碼是為了說明,不需要很多底層通信,URL 也能夠?qū)崿F(xiàn)很多功能。對于其他服務(wù),可能需要處理上傳給 REST 服務(wù)的 XML 結(jié)構(gòu)。XStream 也可以幫助完成這個任務(wù)。例如,要想對包裹的 XML 結(jié)構(gòu)進(jìn)行去序列化,應(yīng)該調(diào)用:
| Bag bag = (Bag) xstream.fromXML(xml); |
客戶機(jī)上的應(yīng)用程序
到目前為止,本文已經(jīng)討論了服務(wù)器端的實現(xiàn)。客戶端上的代碼非常相似。客戶機(jī)可以共享數(shù)據(jù)類?Flight、Traveller?和?Bag,并使用 XStream API 對 XML 進(jìn)行序列化和去序列化。客戶機(jī)上惟一的新部分是連接 URL 并讀取內(nèi)容或發(fā)送內(nèi)容。通過使用 Java 類庫提供的 URL 連接,很容易完成這個任務(wù):
| String xml = "<newinput>input</newinput>"; URL url = new URL("http://luggagetracking.airlinecompany.com/bags/1/newmethod"); URLConnection connection = url.openConnection(); // set POST connection.setDoOutput(true); Writer output = new OutputStreamWriter(connectiongetOutputStream()); output.write(xml); output.close(); // display result BufferedReader input = new BufferedReader( new InputStreamReader(connection.getInputStream())); String decodedString; while ((decodedString = input.readLine()) != null) { System.out.println(decodedString); } input.close(); |
與 Ruby on Rails 等技術(shù)的互操作性
盡管 REST 并沒有明確的規(guī)范來規(guī)定如何實現(xiàn)它,但是對 REST 的開箱即用支持越來越多了。因此,雖然沒有需要遵循的標(biāo)準(zhǔn),但是您需要遵守一些約定。例如,Ruby on Rails 提供 ActiveResource。如果遵守 Rails 對 URL 和輸出格式的約定,就很容易用最小的開銷把 Rails Web 客戶機(jī)連接到 Java REST 式 Web 服務(wù)。
可伸縮性和向重型 SOA 的遷移
隨著應(yīng)用程序環(huán)境的增長,很可能會對越來越多的 REST 實現(xiàn)細(xì)節(jié)進(jìn)行抽象。當(dāng)增長和抽象發(fā)展到一定程度之后,從輕量型技術(shù)遷移到重型的 SOA 技術(shù)可能會節(jié)省成本。這需要把服務(wù)背后的實際業(yè)務(wù)邏輯提取出來,并重新包裝在新環(huán)境中的一個 SOAP 包中,這個過程應(yīng)該不是太難。
尋找應(yīng)用 REST 式 SOA 的機(jī)會
航空公司只是本文使用的一個示例。實際的航空公司規(guī)模都比較大,它們應(yīng)該直接使用重型技術(shù)。如果您為小公司工作,可能需要發(fā)揮想像力,尋找到在實踐中應(yīng)用 SOA 和 REST 原則的最佳方式。花些時間考慮這個問題,這會帶來長遠(yuǎn)的回報!
轉(zhuǎn)載于:https://www.cnblogs.com/goody9807/archive/2012/07/18/2597450.html
總結(jié)
以上是生活随笔為你收集整理的使用 XML 实现 REST 式的 SOA的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c#可移动不规则窗体
- 下一篇: asp.net ajax控件工具集 Au