RESTful Web 服務簡介
REST 在 2000 年由 Roy Fielding 在博士論文中提出,他是 HTTP 規範 1.0 和 1.1 版的首席作者之一。
REST 中最重要的概念是資源(resources),使用全球 ID(通常使用 URI)標識。客戶端應用程式使用 HTTP 方法(GET/
POST/ PUT/ DELETE
)操作資源或資源集。RESTful Web 服務是使用 HTTP 和 REST 原理實現的 Web 服務。通常,RESTful Web 服務應該定義以下方面:
- Web 服務的基/根 URI,比如 http://host/<appcontext>/resources。
- 支援 MIME 型別的響應資料,包括 JSON/XML/ATOM 等等。
-
服務支援的操作集合(例如
POST、GET、PUT
或DELETE
)。
表 1 演示了典型 RESTful Web 服務中使用的資源 URI 和 HTTP 方法。(參考資料 提供了有關 RESTful Web 服務的更多介紹和設計考慮事項。)
表 1. RESTful Web 服務示例
方法/資源 |
資源集合, URI 如: http://host/<appctx>/resources |
成員資源,URI 如: http://host/<appctx>/resources/1234 |
---|---|---|
GET | 列出資源集合的所有成員。 | 檢索標識為 1234 的資源的表示形式。 |
PUT | 使用一個集合更新(替換)另一個集合。 | 更新標記為 1234 的數字資源。 |
POST | 在集合中建立數字資源,其 ID 是自動分配的。 | 在下面建立一個子資源。 |
DELETE | 刪除整個資源集合。 | 刪除標記為 1234 的數字資源。 |
JSR 311 (JAX-RS) 和 Jersey
JSR 311 或 JAX-RS(用於 RESTful Web Services 的 Java API)的提議開始於 2007 年,1.0 版本到 2008 年 10 月定稿。目前,JSR 311 版本 1.1 還處於草案階段。該 JSR 的目的是提供一組 API 以簡化 REST 樣式的 Web 服務的開發。
在 JAX-RS 規範之前,已經有 Restlet 和 RestEasy 之類的框架,可以幫助您實現 RESTful Web 服務,但是它們不夠直觀。Jersey 是 JAX-RS 的參考實現,它包含三個主要部分。
- 核心伺服器(Core Server):通過提供 JSR 311 中標準化的註釋和 API 標準化,您可以用直觀的方式開發 RESTful Web 服務。
- 核心客戶端(Core Client):Jersey 客戶端 API 幫助您與 REST 服務輕鬆通訊。
- 整合(Integration):Jersey 還提供可以輕鬆整合 Spring、Guice、Apache Abdera 的庫。
在本文的以下部分,我介紹了所有這些元件,但是更關注核心伺服器。
構建 RESTful Web 服務
我將從可以整合到 Tomcat 的 “hello world” 應用程式開始。該應用程式將帶領您完成設定環境的過程,並涉及 Jersey 和 JAX-RS 的基礎知識。
然後,我將介紹更加複雜的應用程式,深入探討 JAX-RS 的本質和特性,比如多個 MIME 型別表示形式支援、JAXB 支援等。我將從樣例中摘取一些程式碼片段來介紹重要的概念。
Hello World:第一個 Jersey Web 專案
要設定開發環境,您需要以下內容(見 參考資料 中的下載):
- IDE:Eclipse IDE for JEE (v3.4+) 或 IBM Rational Application Developer 7.5
- Java SE5 或更高版本
- Web 容器:Apache Tomcat 6.0(Jetty 和其他也可以)
- Jersey 庫:Jersey 1.0.3 歸檔,包含所有必需的庫
設定 Jersey 的環境
首先,為 Eclipse 上的 Tomcat 6.0 建立伺服器執行時。這是用於 RESTful Web 應用程式的 Web 容器。然後建立一個名為 “Jersey” 應用程式,並將目標執行時指定為 Tomcat 6.0。
最後,從 Jersey 開發包中將以下庫複製到 WEB-INF 下的庫目錄:
- 核心伺服器:jersey-core.jar,jersey-server.jar,jsr311-api.jar,asm.jar
- 核心客戶端:(用於測試)jersey-client.jar
- JAXB 支援:(在高階樣例中使用)jaxb-impl.jar,jaxb-api.jar,activation.jar,stax-api.jar,wstx-asl.jar
- JSON 支援:(在高階樣例中使用)jersey-json.jar
開發 REST 服務
現在,您已經設定好了開發第一個 REST 服務的環境,該服務對客戶端發出 “Hello”。
要 做到這一點,您需要將所有的 REST 請求傳送到 Jersey 容器 —— 在應用程式的 web.xml 檔案中定義 servlet 排程程式(參見清單 1)。除了宣告 Jersey servlet 外,它還定義一個初始化引數,指示包含資源的 Java 包。
清單 1. 在 web.xml 檔案中定義 Jersey servlet 排程程度
<servlet> <servlet-name>Jersey REST Service</servlet-name> <servlet-class> com.sun.jersey.spi.container.servlet.ServletContainer </servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>sample.hello.resources</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Jersey REST Service</servlet-name> <url-pattern>/rest/*</url-pattern> </servlet-mapping>
現在您將編寫一個名為 HelloResource 的資源,它接受 HTTP GET
並響應
“Hello Jersey”。
清單 2. sample.hello.resources 包中的 HelloResource
@Path("/hello")
public class HelloResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String sayHello() {
return "Hello Jersey";
}
}
該程式碼中有幾個地方需要強調:
- 資源類(Resource Class):注意,資源類是一個簡單的 Java 物件 (POJO),可以實現任何介面。這增加了許多好處,比如可重用性和簡單。
- 註釋(Annotation):在 javax.ws.rs.* 中定義,是 JAX-RS (JSR 311) 規範的一部分。
-
@Path
:定義資源基 URI。由上下文根和主機名組成,資源識別符號類似於 http://localhost:8080/Jersey/rest/hello。 -
@GET:
這意味著以下方法可以響應 HTTPGET
方法。 -
@Produces:
以純文字方式定義響應內容 MIME 型別。
測試 Hello 應用程式
要測試應用程式,可以開啟您的瀏覽器並輸入 URL http://<host>:<port>/<appctx>/rest/hello。您將看到響應 “Hello Jersey”。這非常簡單,使用註釋處理請求、響應和方法。
以下部分將涉及 JAX-RS 規範的必要部分,使用 Contacts 示例應用程式中的程式碼片段進行介紹。您可以在原始碼包中找到這個高階樣例的所有程式碼(參見 下載)。
資源
資源是組成 RESTful Web 服務的關鍵部分。您可以使用 HTTP 方法(如 GET、POST、PUT
和 DELETE
)操作資源。應用程式中的所有內容都是資源:員工、聯絡人、組織等。在
JAX-RX 中,資源通過 POJO 實現,使用 @Path
註釋組成其識別符號。資源可以有子資源。在這種情況下,父資源是資源集合,子資源是成員資源。
在樣例 Contacts 應用程式中,您將操作個人聯絡人和聯絡人集合。ContactsResource
是
/contacts URI 組成的集合資源,ContactResource
是
/contacts/{contactId} URI 組成的成員資源。下劃線 JavaBean 是一個簡單的 Contact 類,使用 id、名稱和地址作為成員欄位。參見清單 3 和清單 4 瞭解詳情。您還可以從本文最後下載完整的程式碼包(參見 下載)。
清單 3. ContactsResource
@Path("/contacts") public class ContactsResource { @Context UriInfo uriInfo; @Context Request request; @GET @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public List<Contact> getContacts() { List<Contact> contacts = >new ArrayList<Contact>(); contacts.addAll( ContactStore.getStore().values() ); return contacts; } @Path("{contact}") public ContactResource getContact( @PathParam("contact") String contact) { return new ContactResource(uriInfo, request, contact); } }
有幾個有趣的地方需要注意。
-
@Context:
使用該註釋注入上下文物件,比如 Request、Response、UriInfo、ServletContext 等。 -
@Path("{contact}"):
這是@Path
註釋,與根路徑 “/contacts” 結合形成子資源的 URI。 -
@PathParam("contact"):
該註釋將引數注入方法引數的路徑,在本例中就是聯絡人 id。其他可用的註釋有@FormParam
、@QueryParam
等。 -
@Produces:
響應支援多個 MIME 型別。在本例和上一個示例中,APPLICATION/XML 將是預設的 MIME 型別。
您也許還注意到了,GET
方法返回定製
Java 物件而不是 String(純文字),正如上一個 Hello World 示例所示。 JAX-RS 規範要求實現支援多個表示形式型別,比如 InputStream、byte[]、JAXB 元素、JAXB 元素集合等等,以及將其序列化為 XML、JSON 或純文字作為響應的能力。下文我將提供更多有關表示形式技術的資訊,尤其是 JAXB 元素表示形式。
清單 4. ContactResource
public class ContactResource { @Context UriInfo uriInfo; @Context Request request; String contact; public ContactResource(UriInfo uriInfo, Request request, String contact) { this.uriInfo = uriInfo; this.request = request; this.contact = contact; } @GET @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Contact getContact() { Contact cont = ContactStore.getStore().get(contact); if(cont==null) throw new NotFoundException("No such Contact."); return cont; } }
ContactResource 的程式碼簡單明瞭。注意以下內容:
-
Representation Type Contact:Contact 是一個簡單的 JavaBean,由
@XmlRootElement
註釋,這使它可以表示為 XML 或 JSON。 - ContactStore:這是基於 HashMap 的記憶體資料儲存庫,其實現對於本文不重要。
方法
HTTP 方法對映到資源的 CRUD(建立、讀取、更新和刪除) 操作。儘管您可以做一些小修改,比如讓 PUT
方法變成建立或更新,但基本的模式如下:
-
HTTP
GET
:獲取/列出/檢索單個資源或資源集合。 -
HTTP
POST
:新建資源。 -
HTTP
PUT
:更新現有資源或資源集合。 -
HTTP
DELETE
:刪除資源或資源集合。
因為我已經介紹過 GET
方法,我將從 POST
開始說明。就像其他方法一樣,我仍然使用
Contact 示例進行說明。
POST
通常通過填寫表單建立新聯絡人。也就是說,HTML 表單將 POST 到伺服器,伺服器建立並維護新建立的聯絡人。清單 5 演示了該操作的伺服器端邏輯。
清單 5. 接受表單提交(POST)並新建一個聯絡人
@POST @Produces(MediaType.TEXT_HTML) @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public void newContact( @FormParam("id") String id, @FormParam("name") String name, @Context HttpServletResponse servletResponse ) throws IOException { Contact c = new Contact(id,name,new ArrayList<Address>()); ContactStore.getStore().put(id, c); URI uri = uriInfo.getAbsolutePathBuilder().path(id).build(); Response.created(uri).build(); servletResponse.sendRedirect("../pages/new_contact.html"); }
注意該示例的以下部分:
-
@Consumes:
宣告該方法使用 HTML FORM。 -
@FormParam:
注入該方法的 HTML 屬性確定的表單輸入。 -
@Response.created(uri).build():
構建新的 URI 用於新建立的聯絡人(/contacts/{id}
)並設定響應程式碼(201/created
)。您可以使用 http://localhost:8080/Jersey/rest/contacts/<id> 訪問新聯絡人。
PUT
我使用 PUT 方法更新現有資源。但是,也可以通過更新實現,或者像清單 6 中的程式碼片段展示的那樣建立一個資源。
清單 6. 接受 PUT 請求並建立或更新聯絡人
@PUT @Consumes(MediaType.APPLICATION_XML) public Response putContact(JAXBElement<Contact> jaxbContact) { Contact c = jaxbContact.getValue(); return putAndGetResponse(c); } private Response putAndGetResponse(Contact c) { Response res; if(ContactStore.getStore().containsKey(c.getId())) { res = Response.noContent().build(); } else { res = Response.created(uriInfo.getAbsolutePath()).build(); } ContactStore.getStore().put(c.getId(), c); return res; }
我還在本示例中包含了許多不同的概念,重點強調以下概念:
-
Consume XML:
putContact()
方法接受 APPLICATION/XML 請求型別,而這種輸入 XML 將使用 JAXB 繫結到 Contact 物件。您將在下一節中找到客戶端程式碼。 -
空響應帶有不同的狀態碼:
PUT
請求的響應沒有任何內容,但是有不同的狀態碼。如果資料儲存庫中存在聯絡人,我將更新該聯絡人並返回204/no content
。如果沒有新聯絡人,我將建立一個並返回201/created
。
DELETE
實現 DELETE
方法非常簡單。示例請檢視清單
7。
清單 7. 刪除其 ID 確定的聯絡人
@DELETE public void deleteContact() { Contact c = ContactStore.getStore().remove(contact); if(c==null) throw new NotFoundException("No such Contact."); }
表示形式
在上一節中,我介紹了幾個表示形式型別。現在我將簡要瀏覽一遍並深入探討 JAXB 表示形式。其他受支援的表示形式有 byte[]、InputStream、File 等。
- String:純文字。
- Response:一般 HTTP 響應,包含帶有不同響應程式碼的定製內容。
- Void:帶有 204/no content 狀態碼的空響應。
- Resource Class:將流程委託給該資源類。
-
POJO:使用
@XmlRootElement
註釋的 JavaBean,這讓它成為一個 JAXB bean,可以繫結到 XML。 - POJO 集合:JAXB bean 集合。
JAX-RS 支援使用 JAXB (Java API for XML Binding) 將 JavaBean 繫結到 XML 或 JSON,反之亦然。JavaBean 必須使用@XmlRootElement
註釋。清單
8 使用 Contact bean 作為示例。沒有明確 @XmlElement
註釋的欄位將包含一個名稱與之相同的
XML 元素。清單 9 顯示了用於一個 Contact bean 的序列化 XML 和 JSON 表示形式。聯絡人集合的表示形式與此相同,預設使用 <Contacts> 作為包裝器元素。
清單 8. Contact bean
@XmlRootElement public class Contact { private String id; private String name; private List<Address> addresses; public Contact() {} public Contact(String id, String name, List<Address> addresses) { this.id = id; this.name = name; this.addresses = addresses; } @XmlElement(name="address") public List<Address> getAddresses() { return addresses; } public void setAddresses(List<Address> addresses) { this.addresses = addresses; } // Omit other getters and setters }
清單 9. 一個 Contact 的表示形式
XML representation: <contact> <address> <city>Shanghai</city> <street>Long Hua Street</street> </address> <address> <city>Shanghai</city> <street>Dong Quan Street</street> </address> <id>huangyim</id> <name>Huang Yi Ming</name> </contact> JSON representation: {"contact":[{"address":[{"city":"Shanghai","street":"Long Hua Street"},{"city":"Shanghai","street":"Dong Quan Street"}],"id":"huangyim","name":"Huang Yi Ming"}]}
對於使用 JAXB 的更高主題,請檢視 參考資料 中的專案主頁。
與 REST 服務通訊的客戶端
在目前為止的示例中,我開發了一個支援 CRUD 的 RESTful Web 服務。現在我開始解釋如何使用 curl 和 Jersey 客戶端 API 與該 REST 服務通訊。這樣一來,我可以測試伺服器端程式碼,並介紹更多有關客戶端技術的資訊。
使用 curl 與 REST 服務通訊
Curl 是一個流行的命令列工具,可以向使用 HTTP 和 HTTPS 協議的伺服器傳送請求。這是一個與 RESTful Web 服務通訊的好工具,因為它可以通過任何 HTTP 方法傳送內容。Curl 已經在 Linux 和 Mac 中自帶了,並且有一個實用工具,可以在 Windows�0�3 平臺上進行安裝(見 參考資料)。
現在,我們初始化獲取所有聯絡人的第一個 curl 命令。您可以參考 清單 3 獲取伺服器端程式碼。
curl http://localhost:8080/Jersey/rest/contacts
響應將使用 XML 幷包含所有聯絡人。
注意,getContacts()
方法還生成一個
application/json MIME 型別響應。您還可以請求該型別的內容。
curl –HAccept:application/json
http://localhost:8080/Jersey/rest/contacts
響應將是一個包含所有聯絡人的 JSON 字串。
現在,我將 PUT
一個新的聯絡人。注意,清單
6 中的 putContact()
方法接受
XML 並使用 JAXB 將 XML 繫結到 Contact 物件。
curl -X PUT -HContent-type:application/xml --data "<contact><id>foo</id>
<name>bar</name></contact>" http://localhost:8080/Jersey/rest/contacts/foo
一個通過 “foo” 識別的新聯絡人將新增到聯絡人儲存庫。您可以使用 URI /contacts 或 /contacts/foo 驗證聯絡人集合或單個聯絡人。
使用 Jersey Client 與 REST 服務通訊
Jersey 還提供了一個客戶端庫,幫助您與伺服器通訊並對 RESTful 服務進行單元測試。該庫是一個一般實現,可以整合任何 HTTP/HTTPS-based Web 服務。
客戶端的核心類是 WebResource
類。您可以使用該類根據根
URI 構建一個請求 URL,然後傳送請求並獲取響應。清單 10 展示瞭如何建立WebResource
例項。注意 WebResource
是一個大物件,因此只建立一次。
清單 10. 建立 WebResource 例項
Client c = Client.create(); WebResource r=c.resource("http://localhost:8080/Jersey/rest/contacts");
第一個 Jersey 客戶端示例將傳送 GET
請求獲取所有聯絡人並列印響應狀態碼和響應內容,參見清單
11。
清單 11. GET 所有聯絡人並列印響應
ClientResponse response = r.get(ClientResponse.class); System.out.println( response.getStatus() ); System.out.println( response.getHeaders().get("Content-Type") ); String entity = response.getEntity(String.class); System.out.println(entity);
清單 12 展示了另一個建立通過 “foo” 識別的新聯絡人的示例。
清單 12. 建立一個聯絡人
Address[] addrs = { new Address("Shanghai", "Ke Yuan Street") }; Contact c = new Contact("foo", "Foo Bar", Arrays.asList(addrs)); ClientResponse response = r .path(c.getId()) .accept(MediaType.APPLICATION_XML) .put(ClientResponse.class, c); System.out.println(response.getStatus());
注意 WebResource
例項的
API。它構建 URI,設定請求頭,並在一行程式碼中呼叫請求。內容(Contact 物件)將自動繫結到 XML。
清單 13 展示了檢索通過 “foo” 識別的聯絡人(已上一個示例中建立)的最後一個示例然後刪除該聯絡人。
清單 13. 檢索 “foo” 聯絡人並刪除
GenericType<JAXBElement<Contact>> generic = new GenericType<JAXBElement<Contact>>() {}; JAXBElement<Contact> jaxbContact = r .path("foo") .type(MediaType.APPLICATION_XML) .get(generic); Contact contact = jaxbContact.getValue(); System.out.println(contact.getId() + ": " + contact.getName()); ClientResponse response = r.path("foo").delete(ClientResponse.class); System.out.println(response.getStatus());
注意,當您想獲取 JAXB bean 響應時,您需要使用 Java 2 Platform, Standard Edition (J2SE) 中引入的範型特性。
使用 Jersey 客戶端練習這些示例。您可以在資源包中找到更多樣例程式碼(見 下載)。還可以參考 Jersey 網站檢視更多資訊(見 參考資料)。
結束語
Jersey 可以使用 Jersey 整合庫與其他框架或實用工具庫整合。目前,Jersey 可以整合 Spring、Guice,還支援 ATOM 表示形式與 apache-adbera 的整合。在 Jersey 專案主頁可以找到 API 和入門指南。
下載
描述 | 名字 | 大小 |
---|---|---|
原始碼 | Jersey.Sample.Contact.Src.zip | 10KB |
參考資料
學習
- 在 Wikipedia 上查詢有關 REST 的介紹和其他相關連結。
- 檢視 Rational Application Developer 試用版。
- 閱讀有關 Jersey client API 的內容以獲取更多資訊。
- 可以從 Java Community Process 獲取更多有關 JAX-RS (JSR 311) 的資訊。
- JAXB Reference Implementation Project 提供了更多有關 JAXB 的資訊。
- developerWorks 技術活動和網路廣播:隨時關注 developerWorks 技術活動和網路廣播。
- developerWorks Web development 專區:通過專門關於 Web 技術的文章和教程,擴充套件您在網站開發方面的技能。
獲得產品和技術
- 從 專案網站 下載 Eclipse。
- 從 公司網站 獲取 Java SE 5.0。
- 從 專案網站 下載 Apache Tomcat。
- 可以在 Jersey 專案主頁 上查詢下載、樣例程式碼歸檔、使用者指南和 JAX-RS API 文件。
- 獲取 curl for Windows。