Web服務和SOA(三)

使用REST協議來實現SOA服務<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

RESTRepresentation State Transfer(表示層有狀態的傳輸協議)的簡稱,它是一種Web架構類型,由Roy Fielding2000年在他的博士論文中提出來的。REST的基本思想是如何充分利用HTTP協議的功能,它特別關注以下內容:

(1)     REST關注資源,即,每種服務都應該設計成對資源進行某種操作;

(2)     REST充分利用了HTTP協議中的謂詞的功能,它不僅使用我們常用的GETPOST謂詞,而且還使用PUTDELETE謂詞

在我們前面使用POX-over-HTTP方法完成的的SOA服務中,我們就使用了POST這個HTTP謂詞。雖然前面我們僅僅列出了幾個服務的源代碼,它們都可以使用POST謂詞來實現,當然,您也可以使用其它謂詞實現這些服務。REST的思想就是,我們爲什麼不充分利用HTTP協議謂詞的功能,把常見的CRUD操作映射到HTTP協議的謂詞上,這樣,消息交換協議就顯得更加清晰。因此,我們可以把CRUD操作和相應的HTTP謂詞進行關聯,具體關係如下表所示:

 

HTTP謂詞

CRUD操作

操作表述

POST

CREATE

Save new resources

GET

READ

Read resources

PUT

UPDATE

Modify existing resources

DELETE

DELETE

Delete resources

 

請您記住,資源及其對應的值代表了系統的狀態,系統狀態的轉換將遵行以下幾個規則:

(1)     POSTPUTDELETE謂詞可以改變資源的狀態

(2)     GET謂詞不能修改資源的狀態

(3)     往系統增加新資源時應該使用POST謂詞

(4)     修改系統中的資源應使用PUT謂詞

(5)     從系統中刪除資源應調用DELETE謂詞

(6)     系統交換協議應該是無狀態的,即本次方法的調用不應依賴於上次的方法調用

這些聽起來很有趣,但資源究竟是什麼?資源其實就是HTTP四個謂詞能覆蓋的請求操作的範圍。拿我們前面的討論過的商品對象來說,它就是一個典型的資源實例。您可以看到,在我們的實現代碼中(見代碼清單5),有一個IF-THEN的判斷語句塊,這個語句塊根據請求中的含有的方法名來執行不同的操作,實際上,我們可以通過使用HTTP謂詞,避免這些條件判斷。那麼,這是如何實現的呢?

如果客戶端想新添一個商品,它需要調用insertItem服務,先要爲這個新商品創建一個XML文檔,然後通過POST方法把這個XML文檔發送到服務器端,服務器的應答可以是一個比較通用的一個結果對象。

代碼清單9— 通用結果對象Outcome的源代碼

@XmlRootElement(name="Outcome")
public class Outcome {
private String retCode;
private String retMessaget;
...
}

服務器端的實現代碼也會因此而減少,因爲現在我們不需要考慮條件判斷語句(請把下面的代碼和代碼清單5相比較)

代碼清單10— REST風格的新建服務的服務器端實現代碼

protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
try{
JAXBContext jaxbContext = JAXBContext.newInstance(Item.class, Outcome.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
//Receiving the XML request and transform it into a Java object
Item item = (Item) unmarshaller.unmarshal(request.getInputStream());

System.out.println("Inserting item# "+item.getId());

// ... insert item
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);

Outcome outcome = new Outcome();
outcome.setRetCode("OK");
outcome.setRetMessaget("Item was inserted successfully");
marshaller.marshal(outcome, response.getOutputStream());
}catch (Exception e) {
throw new ServletException(e);
}}

請注意,上面的代碼只是REST協議的一個非常簡單的手工實現,我們是使用一個簡單的Servlet實現的,因爲我們想把注意力集中在消息交換協議的一些重要概念上。雖然REST可由多種方法實現,比如JAX-WS(我們在討論SOAP時會講到)Axis2都可實現REST

好,現在回到我們的例子,updateItem服務可由與上面類似的代碼實現,唯一的區別就是把update服務的實現代碼放在ServletdoPut方法中,而不是放在doPost方法中。其實,REST方法還有一點與衆不同,我們這裏提前說一下。

代碼清單11— REST風格的新建服務的客戶器端實現代碼

Item item = new Item();
item.set...

//Prepare and establish the connection with the service
URL url = new URL("http://localhost/SoaBookREST/itemService");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoOutput(true);
//Set the HTTP request method
con.setRequestMethod("POST");
con.connect();
JAXBContext jaxbContext = JAXBContext.newInstance(Item.class, Outcome.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);

//Send the XML request to the service
marshaller.marshal(item, con.getOutputStream());

//Get the XML response from the service and deserialize it
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Outcome outcome = (Outcome)
unmarshaller.unmarshal(con.getInputStream());

 

至於deleteItemfindItemByIdfindAllItems服務,REST方法使用的消息交換協議會有些不同,這些操作實際上不需要上傳XML到服務器,它們最多只需要傳給服務器一個Id號,像findAllItems根本就不要傳給服務器任何輸入。在這些情況下,URIHTTP謂詞就能提供服務器需要的所有信息。下表列出了REST請求的一些URI實例:

謂詞

URI示例

操作

DELETE

http://localhost/SoaBookREST/itemService/14

刪除第14號商品

GET

http://localhost/SoaBookREST/itemService/14

獲取第14號商品

GET

http://localhost/SoaBookREST/itemService

獲取所有商品

您會從上表看到,HTTP請求由謂詞加URI構成,它清楚地告訴服務器它要求哪個服務。請您注意,上面的URI包含了一些額外數據(比如Id),因此您需要web描述文件中配置相應的URL映射模式,而不是針對某個具體的URL進行配置,web.xml的示例配置如下:

代碼清單12— web.xmlservlet的映射配置

<servlet-mapping>
<servlet-name>ItemService</servlet-name>
<url-pattern>/itemService/*</url-pattern>
</servlet-mapping>

下面是最後兩個操作的服務實現代碼:

代碼清單13— REST風格讀取服務的實現

protected void doGet(HttpServletRequest request,HttpServletResponse response)
throws ServletException, IOException
{
try{
if (request.getPathInfo()==null){
//findAllItems
ItemList itemList = new ItemList();
itemList.setList(new ArrayList());

//retrieve all items
...
itemList.getList().add(...);
...

//Send the XML message to the client
JAXBContext jaxbContext = JAXBContext.newInstance(ItemList.class, Item.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);
marshaller.marshal(itemList, response.getOutputStream());
} else {

//findItemById
int id = (new Integer(request.getPathInfo().substring(1))).intValue();

//retrieve item by id (e.g. from a database)
Item item = ...
JAXBContext jaxbContext = JAXBContext.newInstance(Item.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);
marshaller.marshal(item, response.getOutputStream());
}
}catch (Exception e) {
throw new ServletException(e);
}}

如果您想更新一個商品,您需要調用如下的REST風格的URI,其中包含了商品的Id信息。

PUT http://localhost/SoaBookREST/itemService/14

其實,REST的這種調用哲學和HTTP請求的自解釋性的特點是一致的,上面的URI可以讀作“更新14號商品”。

一般說來,如果您要使用上面的方法創建非CRUD服務,您有以下兩種選擇:

(1)     使用合適的謂詞,新建一個Servelet

(2)     重用已有的Servlet和謂詞,但需要添加新的代碼,以解析和組裝HTTP請求中的業務邏輯。

您也可以考慮在請求中加入參數這種比較實用的辦法,請求中添加的參數可以讓執行的業務流程發生變化,雖然REST的追隨者不喜歡這樣做。這種方法的請求格式可能如下所示:

http://localhost/SoaBookREST/itemService?id=14

REST追隨者們對上面的辦法可能頗有微詞,實際上,上面的這種請求格式依賴於具體的參數名(id),這多少爲REST這種簡單線性的請求風格增添了複雜性。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章