1. 程式人生 > >使用 Jersey 和 Apache Tomcat 構建 RESTful Web 服務

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

eclipse encode 服務器 form red 了解 響應代碼 知識 -s

RESTful Web 服務簡單介紹

REST 在 2000 年由 Roy Fielding 在博士論文中提出,他是 HTTP 規範 1.0 和 1.1 版的首席作者之中的一個。

REST 中最重要的概念是資源(resources),使用全球 ID(通常使用 URI)標識。

client應用程序使用 HTTP 方法(GET/ POST/ PUT/ DELETE)操作資源或資源集。RESTful Web 服務是使用 HTTP 和 REST 原理實現的 Web 服務。通常,RESTful Web 服務應該定義下面方面:

  • Web 服務的基/根 URI,比方 http://host/<appcontext>/resources。
  • 支持 MIME 類型的響應數據。包含 JSON/XML/ATOM 等等。

  • 服務支持的操作集合(比如 POST、GET、PUTDELETE)。

表 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 的參考實現。它包括三個主要部分。

  • 核心server(Core Server):通過提供 JSR 311 中標準化的凝視和 API 標準化,您能夠用直觀的方式開發 RESTful Web 服務。

  • 核心client(Core Client):Jersey client API 幫助您與 REST 服務輕松通信。
  • 集成(Integration):Jersey 還提供能夠輕松集成 Spring、Guice、Apache Abdera 的庫。

在本文的下面部分,我介紹了全部這些組件,可是更關註核心server。

構建 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 創建server執行時。

這是用於 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 服務的環境。該服務對client發出 “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”。這很easy,使用凝視處理請求、響應和方法。

下面部分將涉及 JAX-RS 規範的必要部分。使用 Contacts 演示例子應用程序中的代碼片段進行介紹。

您能夠在源碼包中找到這個高級例子的全部代碼

資源

資源是組成 RESTful Web 服務的關鍵部分。

您能夠使用 HTTP 方法(如 GET、POST、PUTDELETE)操作資源。

應用程序中的全部內容都是資源:員工、聯系人、組織等。

在 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 到server,server創建並維護新創建的聯系人。

清單 5 演示了該操作的server端邏輯。

清單 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 對象。

    您將在下一節中找到client代碼。

  • 空響應帶有不同的狀態碼:PUT 請求的響應沒有不論什麽內容,可是有不同的狀態碼。假設數據存儲庫中存在聯系人,我將更新該聯系人並返回 204/no content。假設沒有新聯系人。我將創建一個並返回 201/created

DELETE

實現 DELETE 方法很easy。

演示樣例請查看清單 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 服務通訊的client

在眼下為止的演示樣例中,我開發了一個支持 CRUD 的 RESTful Web 服務。

如今我開始解釋怎樣使用 curl 和 Jersey client API 與該 REST 服務通訊。這樣一來,我能夠測試server端代碼,並介紹很多其它有關client技術的信息。

使用 curl 與 REST 服務通訊

Curl 是一個流行的命令行工具,能夠向使用 HTTP 和 HTTPS 協議的server發送請求。這是一個與 RESTful Web 服務通訊的好工具。由於它能夠通過不論什麽 HTTP 方法發送內容。

Curl 已經在 Linux 和 Mac 中自帶了。而且有一個有用工具。能夠在 Windows? 平臺上進行安裝。

如今,我們初始化獲取全部聯系人的第一個 curl 命令。您能夠參考 清單 3 獲取server端代碼。

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 還提供了一個client庫,幫助您與server通訊並對 RESTful 服務進行單元測試。該庫是一個一般實現,能夠整合不論什麽 HTTP/HTTPS-based Web 服務。

client的核心類是 WebResource 類。您能夠使用該類依據根 URI 構建一個請求 URL,然後發送請求並獲取響應。清單 10 展示了怎樣創建WebResource 實例。

註意 WebResource 是一個大對象,因此僅僅創建一次。

清單 10. 創建 WebResource 實例

Client c = Client.create();
WebResource r=c.resource("http://localhost:8080/Jersey/rest/contacts");

第一個 Jersey client演示樣例將發送 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 client練習這些演示例子。

您能夠在資源包中找到很多其它例子代碼。

還能夠參考 Jersey 站點查看很多其它信息。

結束語

Jersey 能夠使用 Jersey 集成庫與其它框架或有用工具庫集成。

眼下,Jersey 能夠集成 Spring、Guice,還支持 ATOM 表示形式與 apache-adbera 的集成。

在 Jersey 項目主頁能夠找到 API 和入門指南。

下載

描寫敘述 名字 大小
源碼 Jersey.Sample.Contact.Src.zip

10KB

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