程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 使用Jersey和Apache Tomcat構建RESTful Web服務

使用Jersey和Apache Tomcat構建RESTful Web服務

編輯:關於JAVA

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 {
 @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:這意味著以下方法可以響應 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 {
  @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"}]}

與 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 可以集成 Spring、Guice,還支持 ATOM 表示形式與 apache-adbera 的集成。在 Jersey 項目主頁可以找到 API 和入門指南。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved