天天看點

JAX-RS的參考實作:《使用 Jersey 和 Apache Tomcat 建構 RESTful Web 服務》

本文由IBMDW的Yi Ming Huang, Dong Fei Wu, Qing Guo等三位IBM軟體工程師聯合撰寫,原文标題為《使用 Jersey 和 Apache Tomcat 建構 RESTful Web 服務》。

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 方法。

表 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 {@[email protected](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:

這意味着以下方法可以響應 HTTP

GET

方法。

@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 {@ContextUriInfo uriInfo;@ContextRequest request;@[email protected]({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 {@ContextUriInfo uriInfo;@ContextRequest request;String contact;public ContactResource(UriInfo uriInfo, Request request, String contact) {this.uriInfo = uriInfo;this.request = request;this.contact = contact;}@[email protected]({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)并建立一個聯系人

@[email protected](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 請求并建立或更新聯系人

@[email protected](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 确定的聯系人

@DELETEpublic 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

@XmlRootElementpublic 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"}]}               

與 REST 服務通訊的用戶端

在目前為止的示例中,我開發了一個支援 CRUD 的 RESTful Web 服務。現在我開始解釋如何使用 curl 和 Jersey 用戶端 API 與該 REST 服務通訊。這樣一來,我可以測試伺服器端代碼,并介紹更多有關用戶端技術的資訊。

使用 curl 與 REST 服務通訊

Curl 是一個流行的指令行工具,可以向使用 HTTP 和 HTTPS 協定的伺服器發送請求。這是一個與 RESTful Web 服務通訊的好工具,因為它可以通過任何 HTTP 方法發送内容。Curl 已經在 Linux 和 Mac 中自帶了,并且有一個實用工具,可以在 Windows 平台上進行安裝。

現在,我們初始化擷取所有聯系人的第一個 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 和入門指南。

http://www.ibm.com/developerworks/cn/web/wa-aj-tomcat/

上一篇: JAX-RS XML
下一篇: JAX-RS介紹