国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
使用 Struts 2 開發(fā) RESTful 服務(wù)

REST 簡介

REST 是英文 Representational State Transfer 的縮寫,這個術(shù)語由 Roy Thomas Fielding 博士在他的論文《Architectural Styles and the Design of Network-based Software Architectures》中提出。從這篇論文的標(biāo)題可以看出:REST 是一種基于網(wǎng)絡(luò)的軟件架構(gòu)風(fēng)格。

提示:國內(nèi)很多網(wǎng)絡(luò)資料將 REST 翻譯為“表述性狀態(tài)轉(zhuǎn)移”,不過筆者對這個翻譯不太認(rèn)同。因為這個專業(yè)術(shù)語無法傳達(dá) REST 的含義,讀者可以先不要理會 REST 到底該如何翻譯,盡量先去理解 REST 是什么?有什么用?然后再來看這個術(shù)語的翻譯。關(guān)于 Roy Thomas Fielding 博士的原文參見如下地址:http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm。

REST 架構(gòu)是針對傳統(tǒng) Web 應(yīng)用提出的一種改進(jìn),是一種新型的分布式軟件設(shè)計架構(gòu)。對于異構(gòu)系統(tǒng)如何進(jìn)行整合的問題,目前主流做法都集中在使用 SOAP、WSDL 和 WS-* 規(guī)范的 Web Services。而 REST 架構(gòu)實際上也是解決異構(gòu)系統(tǒng)整合問題的一種新思路。

如果開發(fā)者在開發(fā)過程中能堅持 REST 原則,將可以得到一個使用了優(yōu)質(zhì) Web 架構(gòu)的系統(tǒng),從而為系統(tǒng)提供更好的可伸縮性,并降低開發(fā)難度。關(guān)于 REST 架構(gòu)的主要原則如下:

  • 網(wǎng)絡(luò)上的所有事物都可被抽象為資源(Resource)。
  • 每個資源都有一個唯一的資源標(biāo)識符(Resource Identifier)。
  • 同一資源具有多種表現(xiàn)形式。
  • 使用標(biāo)準(zhǔn)方法操作資源。
  • 通過緩存來提高性能。
  • 對資源的各種操作不會改變資源標(biāo)識符。
  • 所有的操作都是無狀態(tài)的(Stateless)。

僅從上面幾條原則來看 REST 架構(gòu),其實依然比較難以理解,下面筆者將從如下二個方面來介紹 REST。





回頁首


資源和標(biāo)識符

現(xiàn)在的 Web 應(yīng)用上包含了大量信息,但這些信息都被隱藏在 HTML、CSS 和 JavaScript 代碼中,對于普通瀏覽者而言,他們進(jìn)入這個系統(tǒng)時無法知道該系統(tǒng)包含哪些頁面;對于一個需要訪問該系統(tǒng)資源的第三方系統(tǒng)而言,同樣無法明白這個系統(tǒng)包含多少功能和信息。

URI 和 URL

與 URI 相關(guān)的概念還有 URL,URL 是 Uniform Resource Locator,也就是統(tǒng)一資源定位符的意思。其中 http://www.crazyit.org 就是一個統(tǒng)一資源定位符,URL 是 URI 的子集。簡而言之:每個 URL 都是 URI,但不是每個 URI 都是 URL。

從 REST 架構(gòu)的角度來看,該系統(tǒng)里包含的所有功能和信息,都可被稱為資源(Resource),REST 架構(gòu)中的資源包含靜態(tài)頁面、JSP 和 Servlet 等,該應(yīng)用暴露在網(wǎng)絡(luò)上的所有功能和信息都可被稱為資源。

除此之外,REST 架構(gòu)規(guī)范了應(yīng)用資源的命名方式,REST 規(guī)定對應(yīng)用資源使用統(tǒng)一的命名方式:REST 系統(tǒng)中的資源必須統(tǒng)一命名和規(guī)劃,REST 系統(tǒng)由使用 URI(Uniform Resource Identifier,即統(tǒng)一資源標(biāo)識符)命名的資源組成。由于 REST 對資源使用了基于 URI 的統(tǒng)一命名,因此這些信息就自然地暴露出來了,從而避免 “信息地窖”的不良后果。

對于當(dāng)今最常見的網(wǎng)絡(luò)應(yīng)用來說,資源標(biāo)識符就是 URI,資源的使用者則根據(jù) URI 來操作應(yīng)用資源。當(dāng) URI 發(fā)生改變時,表明客戶機(jī)所使用的資源發(fā)生了改變。

從資源的角度來看,當(dāng)客戶機(jī)操作不同的資源時,資源所在的 Web 頁(將 Web 頁當(dāng)成虛擬的狀態(tài)機(jī)來看)的狀態(tài)就會發(fā)生改變、遷移(Transfer),這就是 REST 術(shù)語中 ST(State Tranfer)的由來了。

客戶機(jī)為了操作不同狀態(tài)的資源,則需要發(fā)送一些 Representational 的數(shù)據(jù),這些數(shù)據(jù)包含必要的交互數(shù)據(jù),以及描述這些數(shù)據(jù)的元數(shù)據(jù)。這就是 REST 術(shù)語中 RE(Representational)的由來了。理解了這個層次之后,至于 REST 如何翻譯、或是否真正給它一個中文術(shù)語,讀者可自行決定。





回頁首


操作資源的方式

對于 REST 架構(gòu)的服務(wù)器端而言,它提供的是資源,但同一資源具有多種表現(xiàn)形式(可通過在 HTTP Content-type 頭中包含關(guān)于數(shù)據(jù)類型的元數(shù)據(jù))。如果客戶程序完全支持 HTTP 應(yīng)用協(xié)議,并能正確處理 REST 架構(gòu)的標(biāo)準(zhǔn)數(shù)據(jù)格式,那么它就可以與世界上任意一個 REST 風(fēng)格的用戶交互。這種情況不僅適用于從服務(wù)器端到客戶端的數(shù)據(jù),反之亦然——倘若從客戶端傳來的數(shù)據(jù)符合 REST 架構(gòu)的標(biāo)準(zhǔn)數(shù)據(jù)格式,那么服務(wù)器端也可以正確處理數(shù)據(jù),而不去關(guān)心客戶端的類型。

典型情況下,REST 風(fēng)格的資源能以 XHTML、XML 和 JSON 三種形式存在,其中 XML 格式的數(shù)據(jù)是 WebServices 技術(shù)的數(shù)據(jù)交換格式,而 JSON 則是另一種輕量級的數(shù)據(jù)交換格式;至于 XHTML 格式則主要由瀏覽器負(fù)責(zé)呈現(xiàn)。當(dāng)服務(wù)器為所有資源提供多種表現(xiàn)形式之后,這些資源不僅可以被標(biāo)準(zhǔn) Web 瀏覽器所使用,還可以由 JavaScript 通過 Ajax 技術(shù)調(diào)用,或者以 RPC(Remote Procedure Call)風(fēng)格調(diào)用,從而變成 REST 風(fēng)格的 WebServices。

REST 架構(gòu)除了規(guī)定服務(wù)器提供資源的方式之外,還推薦客戶端使用 HTTP 作為 Generic Connector Interface(也就是通用連接器接口),而 HTTP 則把對一個 URI 的操作限制在了 4 個之內(nèi):GET、POST、PUT 和 DELETE。通過使用通用連接器接口對資源進(jìn)行操作的好處是保證系統(tǒng)提供的服務(wù)都是高度解耦的,從而簡化了系統(tǒng)開發(fā),改善了系統(tǒng)的交互性和可重用性。

REST 架構(gòu)要求客戶端的所有的操作在本質(zhì)上是無狀態(tài)的,即從客戶到服務(wù)器的每個 Request 都必須包含理解該 Request 的所有必需信息。這種無狀態(tài)性的規(guī)范提供了如下幾點好處:

  • 無狀態(tài)性使得客戶端和服務(wù)器端不必保存對方的詳細(xì)信息,服務(wù)器只需要處理當(dāng)前 Request,而不必了解前面 Request 的歷史。
  • 無狀態(tài)性減少了服務(wù)器從局部錯誤中恢復(fù)的任務(wù)量,可以非常方便地實現(xiàn) Fail Over 技術(shù),從而很容易地將服務(wù)器組件部署在集群內(nèi)。
  • 無狀態(tài)性使得服務(wù)器端不必在多個 Request 中保存狀態(tài),從而可以更容易地釋放資源。
  • 無狀態(tài)性無需服務(wù)組件保存 Request 狀態(tài),因此可讓服務(wù)器充分利用 Pool 技術(shù)來提高穩(wěn)定性和性能。

當(dāng)然,無狀態(tài)性會使得服務(wù)器不再保存 Request 的狀態(tài)數(shù)據(jù),因此需要在一系列 Request 中發(fā)送重復(fù)數(shù)據(jù),從而提高了系統(tǒng)的通信成本。為了改善無狀態(tài)性帶來的性能下降,REST 架構(gòu)填加了緩存約束。緩存約束允許隱式或顯式地標(biāo)記一個 Response 中的數(shù)據(jù),這樣就賦予了客戶端緩存 Response 數(shù)據(jù)的功能,這樣就可以為以后的 Request 共用緩存的數(shù)據(jù),部分或全部的消除一些交互,增加了網(wǎng)絡(luò)的效率。但是用于客戶端緩存了信息,也就同時增加了客戶端與服務(wù)器數(shù)據(jù)不一致的可能,從而降低了可靠性。





回頁首


Struts 2 的 REST 支持

約定優(yōu)于配置

Convention 這個單詞的翻譯過來就是“約定”的意思。有 Ruby On Rails 開發(fā)經(jīng)驗的讀者知道 Rails 有一條重要原則:約定優(yōu)于配置。Rails 開發(fā)者只需要按約定開發(fā) ActiveRecord、ActiveController 即可,無需進(jìn)行配置。很明顯,Struts 2 的 Convention 插件借鑒了 Rails 的創(chuàng)意,甚至連插件的名稱都借鑒了“約定優(yōu)于配置”原則。

從 Struts 2.1 開始,Struts 2 改為使用 Convention 插件來支持零配置。Convention 插件徹底地拋棄了配置信息,不僅不需要使用 struts.xml 文件進(jìn)行配置,甚至不需要使用 Annotation 進(jìn)行配置。而是由 Struts 2 根據(jù)約定來自動配置。

Convention 這個單詞的翻譯過來就是“約定”的意思。有 Ruby On Rails 開發(fā)經(jīng)驗的讀者知道 Rails 有一條重要原則:約定優(yōu)于配置。Rails 開發(fā)者只需要按約定開發(fā) ActiveRecord、ActiveController 即可,無需進(jìn)行配置。很明顯,Struts 2 的 Convention 插件借鑒了 Rails 的創(chuàng)意,甚至連插件的名稱都借鑒了“約定優(yōu)于配置”原則。

由于 Struts 2 的 Convention 插件的主要特點是“約定優(yōu)于配置”,當(dāng)我們已經(jīng)習(xí)慣了 Struts 2 的基本開發(fā)方法之后,如果希望改為使用 Convention 插件也非常容易,我們只要放棄 Stuts 2.1 應(yīng)用原有的配置文件,改為按 Convention 插件的約定來定義 Action 即可。

以 Convention 插件為基礎(chǔ),Struts 2.1 又新增了 REST 插件,允許 Struts 2 應(yīng)用對外提供 REST 服務(wù)。REST 插件也無需使用 XML 進(jìn)行配置管理。Struts 2.1 通過 REST 插件完全可以提供讓人和機(jī)器客戶端共同使用的資源,并支持 Ruby On Rails 風(fēng)格的 URL。





回頁首


RestActionMapper 簡介

從本質(zhì)上來看,Struts 2 依然是一個 MVC 框架,最初設(shè)計 Struts 2 時并沒有按 REST 架構(gòu)進(jìn)行設(shè)計,因此 Struts 2 本質(zhì)上并不是一個 REST 框架。由于 Struts 2 提供了良好的可擴(kuò)展性,因此允許通過 REST 插件將其擴(kuò)展成支持 REST 的框架。REST 插件的核心是 RestActionMapper,它負(fù)責(zé)將 Rails 風(fēng)格的 URL 轉(zhuǎn)換為傳統(tǒng)請求的 URL。

用 WinRAR 打開 struts2-rest-plugin-2.1.6 文件,看到該文件里包含一個 struts-plugin.xml 文件,該文件中包含如下一行:

<!-- 定義支持 REST 的 ActionMapper --><bean type="org.apache.struts2.dispatcher.mapper.ActionMapper"  name="rest" class="org.apache.struts2.rest.RestActionMapper" />			

通過查看 RestActionMapper 的 API 說明,我們發(fā)現(xiàn)它可接受如下幾個參數(shù):

  • struts.mapper.idParameterName:用于設(shè)置 ID 請求參數(shù)的參數(shù)名,該屬性值默認(rèn)是 id。
  • struts.mapper.indexMethodName:設(shè)置不帶 id 請求參數(shù)的 GET 請求調(diào)用 Action 的哪個方法。該屬性值默認(rèn)是 index。
  • struts.mapper.getMethodName:設(shè)置帶 id 請求參數(shù)的 GET 請求調(diào)用 Action 的哪個方法。該屬性值默認(rèn)是 show。
  • struts.mapper.postMethodName:設(shè)置不帶 id 請求參數(shù)的 POST 請求調(diào)用 Action 的哪個方法。該屬性值默認(rèn)是 create。
  • struts.mapper.putMethodName:設(shè)置帶 id 請求參數(shù)的 PUT 請求調(diào)用 Action 的哪個方法。該屬性值默認(rèn)是 update。
  • struts.mapper.deleteMethodName:設(shè)置帶 id 請求參數(shù)的 DELETE 請求調(diào)用 Action 的哪個方法。該屬性值默認(rèn)是 destroy。
  • struts.mapper.editMethodName:設(shè)置帶 id 請求參數(shù)、且指定操作 edit 資源的 GET 請求調(diào)用 Action 的哪個方法。該屬性值默認(rèn)是 edit。
  • struts.mapper.newMethodName:設(shè)置不帶 id 請求參數(shù)、且指定操作 edit 資源的 GET 請求調(diào)用 Action 的哪個方法。該屬性值默認(rèn)是 editNew。

在 RestActionMapper 的方法列表中,我們看到 setIdParameterName、setIndexMethodName、setGetMethodName、setPostMethodName、setPutMethodName、setDeleteMethodName、setEditMethodName、setNewMethodName 等方法,這些方法對應(yīng)為上面列出的方法提供 setter 支持。

通常情況下,我們沒有必要改變 RestActionMapper 的參數(shù),直接使用這些參數(shù)的默認(rèn)值就可支持 Rails 風(fēng)格的 REST。根據(jù)前面介紹可以看出:支持 REST 風(fēng)格的 Action 至少包含如下 7 個方法:

  • index:處理不帶 id 請求參數(shù)的 GET 請求。
  • show:處理帶 id 請求參數(shù)的 GET 請求。
  • create:處理不帶 id 請求參數(shù)的 POST 請求。
  • update:處理帶 id 請求參數(shù)的 PUT 請求。
  • destroy:處理帶 id 請求參數(shù)的 DELETE 請求。
  • edit:處理帶 id 請求參數(shù),且指定操作 edit 資源的 GET 請求。
  • editNew:處理不帶 id 請求參數(shù),且指定操作 edit 資源的 GET 請求。

如果請求需要向服務(wù)器發(fā)送 id 請求參數(shù),直接將請求參數(shù)的值附加在 URL 中即可。表 1 顯示了 RestActionMapper 對不同 HTTP 請求的處理結(jié)果。


表 1. RestActionMapper 對 HTTP 請求的處理
HTTP 方法URI調(diào)用 Action 的方法請求參數(shù)
GET/bookindex 
POST/bookcreate 
PUT/book/2updateid=2
DELETE/book/2destroyid=2
GET/book/2showid=2
GET/book/2/editeditid=2
GET/book/neweditNew 

不幸地是,標(biāo)準(zhǔn) HTML 語言目前根本不支持 PUT 和 DELETE 兩個操作,為了彌補(bǔ)這種不足,REST 插件允許開發(fā)者提交請求時額外增加一個 _method 請求參數(shù),該參數(shù)值可以為 PUT 或 DELETE,用于模擬 HTTP 協(xié)議的 PUT 和 DELETE 操作。





回頁首


為 Struts 2 應(yīng)用安裝 REST 插件

安裝 REST 插件非常簡單,只需按如下步驟進(jìn)行即可:

  1. 將 Struts 2 項目下 struts2-convention-plugin-2.1.6.jar、struts2-rest-plugin-2.1.6.jar 兩個 JAR 包復(fù)制到 Web 應(yīng)用的 WEB-INF\lib 路徑下。
  2. 由于 Struts 2 的 REST 插件還需要將提供 XML、JSON 格式的數(shù)據(jù),因此還需要將 xstream-1.2.2.jar、json-lib-2.1.jar、ezmorph-1.0.3.jar 以及 Jakarta-Common 相關(guān) JAR 包復(fù)制到 Web 應(yīng)用的 WEB-INF/lib 路徑下。
  3. 通過 struts.xml、struts.properties 或 web.xml 改變 struts.convention.default.parent.package 常量的值,讓支持 REST 風(fēng)格的 Action 所在的包默認(rèn)繼承 rest-default,而不是繼承默認(rèn)的 convention-default 父包。

對于第三個步驟而言,開發(fā)者完全可以不設(shè)置該常量,如果開發(fā)者不設(shè)置該常量,則意味著開發(fā)者必須通過 Annotation 為每個 Action 類設(shè)置父包。





回頁首


實現(xiàn)支持 REST 的 Action 類

在實現(xiàn)支持 REST 的 Action 之前,我們先為系統(tǒng)提供一個 Model 類:Book,該 Book 類非常簡單,代碼如下:

public class Book {     private Integer id;     private String name;     private double price;     // 無參數(shù)的構(gòu)造器    public Book(){}     //id 屬性的 setter 和 getter 方法    public void setId(Integer id)     {         this.id = id;     }     public Integer getId()     {         return this.id;     }     // 省略 name 和 price 的 setter 和 getter 方法    ... } 

除了提供上面的 Book 類之外,我們還為該 Book 類提供一個業(yè)務(wù)邏輯組件:BookService。為了簡單起見,BookService 類不再依賴 DAO 組件訪問數(shù)據(jù)庫,而是直接操作內(nèi)存中的 Book 數(shù)組——簡單地說,本系統(tǒng)中狀態(tài)是瞬態(tài)的,沒有持久化保存,應(yīng)用運行過程中這些狀態(tài)一直存在,但一旦重啟該應(yīng)用,則系統(tǒng)狀態(tài)丟失。下面是 BookService 類的代碼:

public class BookService {     private static Map<Integer , Book> books         = new HashMap<Integer , Book>();     // 保留下本圖書的 ID     private static int nextId = 5;     // 以內(nèi)存中的數(shù)據(jù)模擬數(shù)據(jù)庫的持久存儲    static {         books.put(1 , new Book(1             , "瘋狂 Java 講義" , 99));         books.put(2 , new Book(2             , "輕量級 Java EE 企業(yè)應(yīng)用實戰(zhàn)" , 89));         books.put(3 , new Book(3             , "瘋狂 Ajax 講義", 78));         books.put(4 , new Book(4             , "Struts 2 權(quán)威指南" , 79));     }         // 根據(jù) ID 獲取    public Book get(int id)    {         return books.get(id);     }         // 獲取系統(tǒng)中全部圖書    public List<Book> getAll()     {         return new ArrayList<Book>(books.values());     }         // 更新已有的圖書或保存新圖書    public void saveOrUpdate(Book book)    {         // 如果試圖保存的圖書的 ID 為 null,表明是保存新的圖書        if (book.getId() == null)         {             // 為新的圖書分配 ID。            book.setId(nextId++);         }         // 將保存 book         books.put(book.getId() , book);     }         // 刪除圖書    public void remove(int id)    {         books.remove(id);     } } 

從上面粗體字代碼可以看出,BookService 提供了 4 個方法,用于實現(xiàn)對 Book 對象的 CRUD 操作。

下面開始定義支持 REST 的 Action 類了,這個 Action 類與前面介紹 Struts 2 的普通 Action 存在一些差異——因為該 Action 不再用 execute() 方法來處理用戶請求,而是使用前面介紹的 7 個標(biāo)準(zhǔn)方法來處理用戶請求。除此之外,該 Action 總是需要處理 id 請求參數(shù),因此必須提供 id 請求參數(shù),并為之提供對應(yīng)的 setter 和 getter 方法。

因為本系統(tǒng)已經(jīng)提供了 Book Model 類,并且為了更好的模擬 Rails 中 ActiveController(Controller)直接訪問 ActiveRecord(Model)的方式,本系統(tǒng)采用了 ModelDriven 的開發(fā)方式,下面是本系統(tǒng)中支持 REST 的 Action 類的代碼。

 // 定義返回 success 時重定向到 book Action  @Results(@Result(name="success"    , type="redirectAction"    , params = {"actionName" , "book"}))  public class BookController extends ActionSupport     implements ModelDriven<Object>  {     // 封裝 id 請求參數(shù)的屬性    private int id;     private Book model = new Book();     private List<Book> list;     // 定義業(yè)務(wù)邏輯組件    private BookService bookService = new BookService();     // 獲取 id 請求參數(shù)的方法    public void setId(int id)     {         this.id = id;         // 取得方法時順帶初始化 model 對象        if (id > 0)         {             this.model = bookService.get(id);         }     }     public int getId()     {         return this.id;     }     // 處理不帶 id 參數(shù)的 GET 請求    // 進(jìn)入首頁    public HttpHeaders index()     {         list = bookService.getAll();         return new DefaultHttpHeaders("index")             .disableCaching();     }     // 處理不帶 id 參數(shù)的 GET 請求    // 進(jìn)入添加新圖書。    public String editNew()     {         // 創(chuàng)建一個新圖書        model = new Book();         return "editNew";     }     // 處理不帶 id 參數(shù)的 POST 請求    // 保存新圖書    public HttpHeaders create()     {         // 保存圖書        bookService.saveOrUpdate(model);         addActionMessage("添加圖書成功");         return new DefaultHttpHeaders("success")             .setLocationId(model.getId());     }     // 處理帶 id 參數(shù)的 GET 請求    // 顯示指定圖書    public HttpHeaders show()     {         return new DefaultHttpHeaders("show");     }     // 處理帶 id 參數(shù)、且指定操作 edit 資源的 GET 請求    // 進(jìn)入編輯頁面 (book-edit.jsp)     public String edit()     {         return "edit";     }     // 處理帶 id 參數(shù)的 PUT 請求    // 修改圖書    public String update()     {         bookService.saveOrUpdate(model);         addActionMessage("圖書編輯成功!");         return "success";     }     // 處理帶 id 參數(shù),且指定操作 deleteConfirm 資源的方法    // 進(jìn)入刪除頁面 (book-deleteConfirm.jsp)     public String deleteConfirm()     {         return "deleteConfirm";     }     // 處理帶 id 參數(shù)的 DELETE 請求    // 刪除圖書    public String destroy()     {         bookService.remove(id);         addActionMessage("成功刪除 ID 為" + id + "的圖書!");         return "success";     }     // 實現(xiàn) ModelDriven 接口必須實現(xiàn)的 getModel 方法    public Object getModel()     {         return (list != null ? list : model);     }  } 

上面 Action 代碼中粗體字代碼定義了 7 個方法,這 7 個方法正是前面提到的標(biāo)準(zhǔn)方法。除此之外,該 Action 里還包含一個額外的 deleteConfirm() 方法,這個方法用于處理帶 id 參數(shù)、且指定操作 deleteConfirm 資源的 GET 請求。也就是說,當(dāng)用戶請求 /book/1/deleteConfirm 時,該請求將由該方法負(fù)責(zé)處理。實際上,RestActionMapper 不僅可以將對 /book/1/edit 的請求映射到 Book 控制器的 edit() 方法,而 1 將作為 id 請求參數(shù)。實際上,它可以將任意 /book/1/xxx 的請求映射到 Book 控制器的 xxx() 方法,而 1 是請求參數(shù)。上面 Action 類使用了 @Results 進(jìn)行修飾,這表明當(dāng) Action 的任何方法返回“success”邏輯視圖時,系統(tǒng)將重定向到 book.action。

可能有讀者會對 index()、create()、show() 三個方法的返回值感到疑惑:它們不再直接返回普通字符串作為邏輯視圖名字,而是返回一個以字符串為參數(shù)的 DefaultHttpHeaders 對象?其實讀者不必對 DefaultHttpHeaders 感到疑惑,其實 DefaultHttpHeaders 只是普通字符串的加強(qiáng)形式,用于 REST 對處理結(jié)果進(jìn)行更多額外的控制。當(dāng) Action 類的處理方法返回字符串作為邏輯視圖時,Struts 2 只能將其當(dāng)成一個簡單的視圖名,僅能根據(jù)該視圖名映射到實際視圖資源,僅此而已。如果使用 DefaultHttpHeaders 作為邏輯視圖,DefaultHttpHeaders 除了可以包含普通字符串作為邏輯視圖名之外,還可以額外增加更多的控制數(shù)據(jù),從而可以增強(qiáng)對 Response 的控制。關(guān)于 HttpHeaders 和 DefaultHttpHeaders 的介紹請參考 REST 插件的 API。

還有一點需要指出,上面的 BookController 控制器實現(xiàn)類的類名并不以 Action 結(jié)尾,而是以 Controller 結(jié)尾,因此我們可以在 struts.xml 文件中配置如下常量:

 <!--  指定控制器類的后綴為 Controller -->  <constant name="struts.convention.action.suffix"    value="Controller"/> 本應(yīng)用里的 struts.xml 文件如下:程序清單:codes\12\12.6\BookShow\WEB-INF\src\struts.xml  <?xml version="1.0" encoding="GBK" ?>  <!-- 指定 Struts 2 配置文件的 DTD 信息 -->  <!DOCTYPE struts PUBLIC     "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"    "http://struts.apache.org/dtds/struts-2.1.dtd">  <!-- 指定 Struts 2 配置文件的根元素 -->  <struts>     <constant name="struts.i18n.encoding" value="GBK"/>     <!--  指定控制器類的后綴為 Controller -->     <constant name="struts.convention.action.suffix"        value="Controller"/>     <constant name="struts.convention.action.mapAllMatches"        value="true"/>     <!-- 指定 Action 所在包繼承的父包 -->     <constant name="struts.convention.default.parent.package"        value="rest-default"/>  </struts> 





回頁首


實現(xiàn)視圖層

定義了上面 Action 之后,接下來應(yīng)該為這些 Action 提供視圖頁面了,根據(jù) Convention 插件的約定,所有視圖頁面都應(yīng)該放在 WEB-INF\content 目錄下,例如當(dāng)用戶向 /book.action 發(fā)送請求時,該請求將由 BookController 的 index() 方法進(jìn)行處理,該方法處理結(jié)束后返回“index”字符串,也就是將會使用 WEIN-INF\content\book-index.jsp 頁面作為視圖資源。下面是 book-index.jsp 頁面的代碼:

 <%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>  <%@taglib prefix="s" uri="/struts-tags" %>  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  <html xmlns="http://www.w3.org/1999/xhtml">  <head>     <title> 圖書展示系統(tǒng) </title>     <link href="<%=request.getContextPath() %>/css/demo.css"        rel="stylesheet" type="text/css" />  </head>  <body>  <s:actionmessage />  <table>     <tr>         <th> 圖書 ID</th>         <th> 書名 </th>         <th> 價格 </th>         <th> 操作 </th>     </tr>     <s:iterator value="model">     <tr>         <td><s:property value="id"/></td>         <td>${name}</td>         <td>${price}</td>         <td><a href="book/${id}"> 查看 </a> |         <a href="book/${id}/edit"> 編輯 </a> |         <a href="book/${id}/deleteConfirm"> 刪除 </a></td>     </tr>     </s:iterator>  </table>  <a href="<%=request.getContextPath() %>/book/new"> 創(chuàng)建新圖書 </a>  </body>  </html> 

上面 JSP 頁面非常簡單,它負(fù)責(zé)迭代輸出 Action 里包含的集合數(shù)據(jù),向該應(yīng)用 book.action 發(fā)送請求將看到如圖 1 所示頁面。


圖 1 使用 Struts 2 開發(fā)的 REST 服務(wù)
 

Struts 2 的 REST 插件支持一種資源具有多少表示形式,當(dāng)瀏覽者向 book.xml 發(fā)送請求將可以看到如圖 2 所示頁面。


圖 2 REST 服務(wù)的 XML 形式
 

從圖 2 可以看出,該頁面正是 Action 所包含的全部數(shù)據(jù),當(dāng)使用 XML 顯示時 REST 插件將會負(fù)責(zé)把這些數(shù)據(jù)轉(zhuǎn)換成 XML 文檔。

除此之外,REST 插件還提供了 JSON 格式的顯示方式,當(dāng)開發(fā)者向 book.json 發(fā)送請求將看到如圖 3 所示頁面。


圖 3 REST 服務(wù)的 JSON 形式
 

Struts 2 的 REST 插件默認(rèn)支持 XHTML、XML 和 JSON 三種形式的數(shù)據(jù)。

當(dāng)瀏覽者單擊頁面右邊的“編輯”鏈接,將會向 book/idVal/edit 發(fā)送請求,這是一個包含 ID 請求參數(shù)、且指定操作 edit 資源的請求,因此將由 BookController 的 edit() 方法負(fù)責(zé)處理,處理結(jié)束后進(jìn)入 book-edit.jsp 頁面。瀏覽器里將看到如圖 4 所示頁面。


圖 4 編輯指定圖書
 

該頁面單擊“修改”按鈕時需要修改圖書信息,也就是需要使用 PUT 操作,但由于 HTML 不支持 PUT 操作,因此需要為該表單頁增加一個額外的請求參數(shù):_method,該請求參數(shù)的值為 put。該表單頁的代碼如下:

 <%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>  <%@taglib prefix="s" uri="/struts-tags" %>  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  <html xmlns="http://www.w3.org/1999/xhtml">  <head>     <title> 編輯 ID 為 <s:property value="id"/> 的圖書 </title>     <link href="<%=request.getContextPath() %>/css/demo.css"        rel="stylesheet" type="text/css" />  </head>  <body>  <s:form method="post"     action="%{#request.contextPath}/book/%{id}">  <!-- 增加 _method 請求參數(shù),參數(shù)值為 put 用于模擬 PUT 操作 -->  <s:hidden name="_method" value="put" />  <table>     <s:textfield name="id" label="圖書 ID" disabled="true"/>     <s:textfield name="name" label="書名"/>     <s:textfield name="price" label="價格" />     <tr>         <td colspan="2">             <s:submit value="修改"/>         </td>  </table>  </s:form>  <a href="<%=request.getContextPath() %>/book"> 返回首頁 </a>  </body>  </html> 

該表單將提交給 BookController 的 update() 方法處理,update() 方法將負(fù)責(zé)修改系統(tǒng)里指定 ID 對應(yīng)的圖書信息。

與之類似的是,當(dāng)請求需要執(zhí)行 DELETE 操作時,一樣需要增加名為 _method 的請求參數(shù),并將該請求參數(shù)值設(shè)置為 delete。



參考資料



關(guān)于作者

李剛,從事 Java EE 應(yīng)用開發(fā)近 10 年。曾任 LITEON 公司的 J2EE 技術(shù)主管,負(fù)責(zé)該公司的企業(yè)信息化平臺的架構(gòu)設(shè)計。曾任廣州電信、廣東龍泉科技等公司的技術(shù)培訓(xùn)教師。 瘋狂 Java 聯(lián)盟(http://www.crazyit.org)站長。瘋狂 Java 實訓(xùn)營創(chuàng)始人,瘋狂 Java 體系圖書作者,曾任東方標(biāo)準(zhǔn)廣州中心軟件教學(xué)總監(jiān),曾兼任廣東技術(shù)師范學(xué)院計算機(jī)科學(xué)系的兼職副教授。國內(nèi)知名IT技術(shù)作家,已出版《瘋狂 Java 講義》、《輕量級 Java EE 企業(yè)應(yīng)用實戰(zhàn)》、《瘋狂 Ajax 講義》、《Struts 2.1 權(quán)威指南》、《Ruby On Rails 敏捷開發(fā)最佳實踐》等著作。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
什么才是真正的 RESTful 架構(gòu)?
使用Spring MVC構(gòu)建REST風(fēng)格WEB應(yīng)用
使用zend framework 搭建網(wǎng)站(十二)
學(xué)習(xí) REST
最佳實踐:更好的設(shè)計你的 REST API
SSH實現(xiàn)的增刪改查實例
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服