1、網站即軟件,網站開發,完全可以採用軟件開發的模式。但是傳統上,軟件和網絡是兩個不同的領域,很少有交集;軟件開發主要針對單機環境,網絡則主要研究系統之間的通信。互聯網的興起,使得這兩個領域開始融合,現在我們必須考慮,如何開發在互聯網環境中使用的軟件。RESTful架構,就是目前最流行的一種互聯網軟件架構。它結構清晰、符合標準、易於理解、擴展方便,所以正得到越來越多網站的採用。
2、如果一個架構符合REST原則,就稱它爲RESTful架構。要理解RESTful架構,最好的方法就是去理解Representational State Transfer這個詞組到底是什麼意思,它的每一個詞代表了什麼涵義。如果你把這個名稱搞懂了,也就不難體會REST是一種什麼樣的設計。
下面我們對REST進行拆分講解:
第一部分:資源(Resources)
REST的名稱"表現層狀態轉化"中,省略了主語。"表現層"其實指的是"資源"(Resources)的"表現層"。
所謂"資源",就是網絡上的一個實體,或者說是網絡上的一個具體信息。它可以是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的實體。你可以用一個URI(統一資源定位符)指向它,每種資源對應一個特定的URI。要獲取這個資源,訪問它的URI就可以,因此URI就成了每一個資源的地址或獨一無二的識別符。
所謂"上網",就是與互聯網上一系列的"資源"互動,調用它的URI。
第二部分:表現層(Representation)
"資源"是一種信息實體,它可以有多種外在表現形式。我們把"資源"具體呈現出來的形式,叫做它的"表現層"(Representation)。比如,文本可以用txt格式表現,也可以用HTML格式、XML格式、JSON格式表現,甚至可以採用二進制格式;圖片可以用JPG格式表現,也可以用PNG格式表現。URI只代表資源的實體,不代表它的形式。嚴格地說,有些網址最後的".html"後綴名是不必要的,因爲這個後綴名錶示格式,屬於"表現層"範疇,而URI應該只代表"資源"的位置。它的具體表現形式,應該在HTTP請求的頭信息中用Accept和Content-Type字段指定,這兩個字段纔是對"表現層"的描述。
第三部分:狀態轉化(State Transfer)
訪問一個網站,就代表了客戶端和服務器的一個互動過程。在這個過程中,勢必涉及到數據和狀態的變化。互聯網通信協議HTTP協議,是一個無狀態協議。這意味着,所有的狀態都保存在服務器端。因此,如果客戶端想要操作服務器,必須通過某種手段,讓服務器端發生"狀態轉化"(State Transfer)。而這種轉化是建立在表現層之上的,所以就是"表現層狀態轉化"。客戶端用到的手段,只能是HTTP協議。具體來說,就是HTTP協議裏面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET用來獲取資源,POST用來新建資源(也可以用於更新資源),PUT用來更新資源,DELETE用來刪除資源。
綜合上面的解釋,我們總結一下什麼是RESTful架構:
(1)每一個URI代表一種資源;(2)客戶端和服務器之間,傳遞這種資源的某種表現層(Representation);
(3)客戶端通過四個HTTP動詞,對服務器端資源進行操作,實現"表現層狀態轉化"。
3、REST,即Representational State Transfer的縮寫。我對這個詞組的翻譯是"表現層狀態轉化"。REST是一種風格,而不是標準。REST通常基於使用HTTP,URI,和XML以及HTML這些現有的廣泛流行的協議和標準。
a.資源是由URI來指定。
b.對資源的操作包括獲取、創建、修改和刪除資源,這些操作正好對應HTTP協議提供的GET、POST、PUT和DELETE方法。
c.通過操作資源的表現形式來操作資源。
d.資源的表現形式則是XML或者HTML,取決於讀者是機器還是人,是消費web服務的客戶軟件還是web瀏覽器。當然也可以是任何其他的格式。
4、雖然REST受Web技術的影響很深,但是理論上REST架構風格並非綁定在HTTP上。然而,HTTP是唯一與REST相關的實例。基於該原因,本文描述了通過HTTP實現的REST,通常它也被稱爲RESTful HTTP。
5、任何重要的資源都應該能夠通過一個唯一的標識被訪問。RESTful HTTP使用URI來識別資源。URI提供了Web通用的識別機制,它包含了客戶端直接與被引用的資源進行交互時需要的所有信息。
6、通過 REST 風格體系架構,請求和響應都是基於資源表示的傳輸來構建的。資源是通過全局 ID 來標識的,這些 ID 一般使用的是一個統一資源標識符(URI)。客戶端應用使用 HTTP 方法(如,GET、POST、PUT 或 DELETE)來操作一個或多個資源。通常,GET 是用於獲取或列出一個或多個資源,POST 用於創建,PUT 用於更新或替換,而 DELETE 則用於刪除資源。
7、RESTful Web Service 是一個使用 HTTP 和 REST 原理實現的 Web Service。通常,一個 RESTful Web Service 將定義基本資源 URI、它所支持的表示/響應 MIME,以及它所支持的操作。
8、目前在三種主流的Web服務實現方案中,因爲REST模式的Web服務與複雜的SOAP和XML-RPC對比來講明顯的更加簡潔,越來越多的web服務開始採用REST風格設計和實現。其中,SOAP協議:簡單對象訪問協議(SOAP,全寫爲Simple Object Access Protocol)是一種標準化的通訊規範,主要用於Web服務(web service)中。SOAP的出現是爲了簡化網頁服務器(Web Server)在從XML數據庫中提取數據時,無需花時間去格式化頁面,並能夠讓不同應用程序之間透過HTTP通訊協定,以XML格式互相交換彼此的數據,使其與編程語言、平臺和硬件無關。XML-RPC是一個遠程過程調用(遠端程序呼叫)(remote procedure call,RPC)的分佈式計算協議,通過XML將調用函數封裝,並使用HTTP協議作爲傳送機制。後來在新的功能不斷被引入下,這個標準慢慢演變成爲今日的SOAP協定。
9、REST的優點
可以利用緩存Cache來提高響應速度通訊本身的無狀態性可以讓不同的服務器的處理一系列請求中的不同請求,提高服務器的擴展性瀏覽器即可作爲客戶端,簡化軟件需求相對於其他疊加在HTTP協議之上的機制,REST的軟件依賴性更小不需要額外的資源發現機制在軟件技術演進中的長期的兼容性更好10、創建 Java 的 RESTful Web Services有幾種,如 Restlet、RestEasy 和 Jersey。Jersey 是其中最值得注意的,它是 JAX-RS(JSR 311)的參考實現。下面我們就來講解一下通過此技術結合MyEclipse實現REST的過程:
File=>New=>Web Service Project。
project name: restblog,選擇REST(JAX-RS) 下一步。
勾選core,jaxb,client,json然後下一步。
之所以創建servlet是因爲會請求一些物理不存在的URI,事實上就是這個servlet在處理這些resource。
jaxb(Java Architecture for XML Binding)可以將java class序列號成xml,請參考http://java.sun.com/developer/technicalArticles/WebServices/jaxb/
json就不多說了,這個包可以實現java class 轉化成json。
點擊finish完成工程創建。
這樣創建的工程與通常的web工程的區別就是添加了servlet在web.xml,這裏貼出servlet代碼: <servlet>
<display-name>JAX-RS REST Servlet</display-name>
<servlet-name>JAX-RS REST Servlet</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JAX-RS REST Servlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
同時,MyEcipse也會把相應的包添加至工程中,如下:
接下來我們創建一個簡單的類Category
接下來我們新建一個Web Service,如圖:
進入界面:
點擊“Next>”
Java class : CategoryService
URL path: 資源路徑 填寫category ,這樣所有與category相關的資源都使用http://yourpath/services/category訪問
LIfecycle: Pre-request(JAX-RS default)每次請求時生成一個服務類實例(這裏指CategoryService),singleton:單例,只存在一個實例
Consumes: 接受的contentType 包括application/xml ,application/json 通俗來說就是接受提交數據的格式。
Produces: 產生的數據格式選項同樣爲applicatin/xml,application/json 等。
注意這裏的Consumes/Produces是指請求http://yourpath/services/category的數據格式。
點擊 Add 按鈕添加服務方法,界面如下:
是一個構造服務方法的界面,下面有代碼預覽。在添加參數的時候,選擇Param Type選項的意義:
Context: 這是一個標識該參數爲請求上下文。可以直接獲得request的參數。
QueryParam:uri?之後的參數
PathParam:uri中的參數如:/category/{id}中的id就是PathParam
FormParam:使用post提交的參數。
CookieParam: Cookie參數。
HeaderParam: 請求的頭部信息。
也許你已經注意到沒有session參數,是的,所謂的"無狀態stateless"多少就體現在這裏,web服務不保存請求相關的信息。
最後,生成的代碼爲:@Path("category")
public class CategoryService {
@PUT
@Path("add")
@Produces("text/html")
@Consumes({ "application/xml", "application/json" })
public String addCategory(@Context Category category) {
throw new UnsupportedOperationException("Not yet implemented.");
}
}
之後,你也可以在CategoryService.java編輯中 右鍵菜單=>MyEclipse=>Add REST Method添加服務方法。右鍵工程根目錄restblog選擇MyEclipse=>Test with RESTful WebServices Explorer 將會打開測試窗口。
瀏覽中的地址爲:http://localhost:8080/restblog/services/application.wadl 首先/services是由servlet攔截。
如果在瀏覽器中瀏覽該地址,將得到一個xml文件。這個文件叫web service application description language。顧名思義他是一個服務描述文件。
需要說明的是當調用方法的參數爲Category類型,調用時傳遞的是xml同時contentType設置爲application/xml這樣jersey會將xml轉化爲Category類型的對象。如果轉化出錯則服務調用失敗,會返回錯誤信息。
綜合上面的用法,下面的例子涵蓋了常用的服務方法的寫法,之中的處理過程暫時用硬編碼。
package com.zlb.rest.service;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
@Path("category")
public class CategoryService {
@GET
@Produces({ "application/json", "application/xml" })
/**
* getCategories 產生json,xml兩種數據格式,具體那種格式取決於contentType
*/
public List<Category> getCategories() {
List<Category> result = new ArrayList<Category>();
result.add(new Category(1, "第一個分類"));
result.add(new Category(2, "第二個分類"));
return result;
}
@GET
@Path("{id}")
public Category getCategory(@PathParam("id") int id) {
return new Category(id, "id爲" + id + "的類別");
}
@GET
@Path("json/{id}")
@Produces("application/json")
public JSONObject getCategoryJson(@PathParam("id") int id) {// 產生json
JSONObject o = new JSONObject();
try {
o.put("id", id);
o.put("name", "id爲" + id + "的category");
} catch (JSONException e) {
e.printStackTrace();
}
return o;
}
@PUT
@Path("add")
@Produces("text/html")
@Consumes({ "application/xml", "application/json" })
public String addCategory(Category category) {
System.out.println("處理添加類別邏輯,接受的數據爲id:" + category.getId() + ",name:"
+ category.getName());
return "ok";
}
@POST
@Path("addbyname")
public String addCategory(
@FormParam("categoryname") @DefaultValue("[未命名]") String cateogryname) {
System.out.println("處理添加類別邏輯,接受的數據爲name:" + cateogryname);
return "添加類別" + cateogryname + "成功";
}
@POST
@Produces("text/html")
@Path("updatecategory")
@Consumes({ "application/xml", "application/json" })
public String updateCategory(Category category) {
System.out.println("處理更新類別邏輯,接受的數據爲id:" + category.getId() + ",name:"
+ category.getName());
return "ok";
}
@DELETE
@Path("delete/{id}")
public String deleteCategory(@PathParam("id") int id) {
System.out.println("處理刪除類別邏輯,接受的數據爲id:" + id);
return "ok";
}
@GET
@Path("commonProcess")
public String commonProcess(@Context UriInfo info) {// @Context 參數標識UriInfo
StringBuilder buf = new StringBuilder();
for (String param : info.getQueryParameters().keySet()) {
buf.append(param + " : " + info.getQueryParameters().get(param));
buf.append("\n");
}
System.out.println(buf.toString());
return "ok";
}
}
參考資料:http://zh.wikipedia.org/wiki/REST
http://www.ibm.com/developerworks/cn/web/wa-spring3webserv/index.html?ca=drs#download
http://www.blogjava.net/Hafeyang/archive/2009/02/05/253458.html
http://kb.cnblogs.com/page/114905/
http://www.infoq.com/cn/articles/designing-restful-http-apps-roth
http://www.oschina.net/news/27712/10-things-you-should-do-to-write-effective-restful-web-services/