簡介: Representational state transfer(REST)在 2000 年由 Roy Fielding 在博士論文中提出。可是,在 Java™ 社區中,直到 2008 年 JSR 311(JAX-RS) 規範定稿後纔將其標準化。第一個版本的參考實現甚至更晚。在本文中,我將介紹 Jersey —— JSR 311 的參考實現,描述其必要的 API 和註釋。我還將展現如何經過在 Apache Tomcat 中集成 Jersey 從 servlet 風格的服務轉型到 RESTful 服務。html
REST 在 2000 年由 Roy Fielding 在博士論文中提出,他是 HTTP 規範 1.0 和 1.1 版的首席做者之一。apache
REST 中最重要的概念是資源(resources),使用全球 ID(一般使用 URI)標識。客戶端應用程序使用 HTTP 方法(GET/ POST/ PUT/ DELETE
)操做資源或資源集。RESTful Web 服務是使用 HTTP 和 REST 原理實現的 Web 服務。一般,RESTful Web 服務應該定義如下方面:json
POST、GET、PUT
或 DELETE
)。表 1 演示了典型 RESTful Web 服務中使用的資源 URI 和 HTTP 方法。(參考資料 提供了有關 RESTful Web 服務的更多介紹和設計考慮事項。)api
方法/資源 | 資源集合, URI 如: http://host/<appctx>/resources |
成員資源,URI 如: http://host/<appctx>/resources/1234 |
GET | 列出資源集合的全部成員。 | 檢索標識爲 1234 的資源的表示形式。 |
PUT | 使用一個集合更新(替換)另外一個集合。 | 更新標記爲 1234 的數字資源。 |
POST | 在集合中建立數字資源,其 ID 是自動分配的。 | 在下面建立一個子資源。 |
DELETE | 刪除整個資源集合。 | 刪除標記爲 1234 的數字資源。 |
回頁首瀏覽器
JSR 311 (JAX-RS) 和 Jerseytomcat
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 的參考實現,它包含三個主要部分。網絡
在本文的如下部分,我介紹了全部這些組件,可是更關注核心服務器。
我將從能夠集成到 Tomcat 的 「hello world」 應用程序開始。該應用程序將帶領您完成設置環境的過程,並涉及 Jersey 和 JAX-RS 的基礎知識。
而後,我將介紹更加複雜的應用程序,深刻探討 JAX-RS 的本質和特性,好比多個 MIME 類型表示形式支持、JAXB 支持等。我將從樣例中摘取一些代碼片斷來介紹重要的概念。
要設置開發環境,您須要如下內容(見 參考資料 中的下載):
首先,爲 Eclipse 上的 Tomcat 6.0 建立服務器運行時。這是用於 RESTful Web 應用程序的 Web 容器。而後建立一個名爲 「Jersey」 應用程序,並將目標運行時指定爲 Tomcat 6.0。
最後,從 Jersey 開發包中將如下庫複製到 WEB-INF 下的庫目錄:
如今,您已經設置好了開發第一個 REST 服務的環境,該服務對客戶端發出 「Hello」。
要作到這一點,您須要將全部的 REST 請求發送到 Jersey 容器 —— 在應用程序的 web.xml 文件中定義 servlet 調度程序(參見清單 1)。除了聲明 Jersey servlet 外,它還定義一個初始化參數,指示包含資源的 Java 包。
<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」。
@Path("/hello") public class HelloResource { @GET @Produces(MediaType.TEXT_PLAIN) public String sayHello() { return "Hello Jersey"; } } |
該代碼中有幾個地方須要強調:
@Path
:定義資源基 URI。由上下文根和主機名組成,資源標識符相似於 http://localhost:8080/Jersey/rest/hello。@GET:
這意味着如下方法能夠響應 HTTP GET
方法。@Produces:
以純文本方式定義響應內容 MIME 類型。要測試應用程序,能夠打開您的瀏覽器並輸入 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 瞭解詳情。您還能夠從本文最後下載完整的代碼包(參見 下載)。
@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 元素表示形式。
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 的代碼簡單明瞭。注意如下內容:
@XmlRootElement
註釋,這使它能夠表示爲 XML 或 JSON。HTTP 方法映射到資源的 CRUD(建立、讀取、更新和刪除) 操做。儘管您能夠作一些小修改,好比讓 PUT
方法變成建立或更新,但基本的模式以下:
GET
:獲取/列出/檢索單個資源或資源集合。POST
:新建資源。PUT
:更新現有資源或資源集合。DELETE
:刪除資源或資源集合。由於我已經介紹過 GET
方法,我將從 POST
開始說明。就像其餘方法同樣,我仍然使用 Contact 示例進行說明。
一般經過填寫表單建立新聯繫人。也就是說,HTML 表單將 POST 到服務器,服務器建立並維護新建立的聯繫人。清單 5 演示了該操做的服務器端邏輯。
@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 方法更新現有資源。可是,也能夠經過更新實現,或者像清單 6 中的代碼片斷展現的那樣建立一個資源。
@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; } |
我還在本示例中包含了許多不一樣的概念,重點強調如下概念:
putContact()
方法接受 APPLICATION/XML 請求類型,而這種輸入 XML 將使用 JAXB 綁定到 Contact 對象。您將在下一節中找到客戶端代碼。PUT
請求的響應沒有任何內容,可是有不一樣的狀態碼。若是數據存儲庫中存在聯繫人,我將更新該聯繫人並返回 204/no content
。若是沒有新聯繫人,我將建立一個並返回 201/created
。實現 DELETE
方法很是簡單。示例請查看清單 7。
@DELETE public void deleteContact() { Contact c = ContactStore.getStore().remove(contact); if(c==null) throw new NotFoundException("No such Contact."); } |
在上一節中,我介紹了幾個表示形式類型。如今我將簡要瀏覽一遍並深刻探討 JAXB 表示形式。其餘受支持的表示形式有 byte[]、InputStream、File 等。
@XmlRootElement
註釋的 JavaBean,這讓它成爲一個 JAXB bean,能夠綁定到 XML。JAX-RS 支持使用 JAXB (Java API for XML Binding) 將 JavaBean 綁定到 XML 或 JSON,反之亦然。JavaBean 必須使用 @XmlRootElement
註釋。清單 8 使用 Contact bean 做爲示例。沒有明確 @XmlElement
註釋的字段將包含一個名稱與之相同的 XML 元素。清單 9 顯示了用於一個 Contact bean 的序列化 XML 和 JSON 表示形式。聯繫人集合的表示形式與此相同,默認使用 <Contacts> 做爲包裝器元素。
@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 } |
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 的更高主題,請查看 參考資料 中的項目主頁。
在目前爲止的示例中,我開發了一個支持 CRUD 的 RESTful Web 服務。如今我開始解釋如何使用 curl 和 Jersey 客戶端 API 與該 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 還提供了一個客戶端庫,幫助您與服務器通信並對 RESTful 服務進行單元測試。該庫是一個通常實現,能夠整合任何 HTTP/HTTPS-based Web 服務。
客戶端的核心類是 WebResource
類。您可使用該類根據根 URI 構建一個請求 URL,而後發送請求並獲取響應。清單 10 展現瞭如何建立 WebResource
實例。注意 WebResource
是一個大對象,所以只建立一次。
Client c = Client.create(); WebResource r=c.resource("http://localhost:8080/Jersey/rest/contacts"); |
第一個 Jersey 客戶端示例將發送 GET
請求獲取全部聯繫人並打印響應狀態碼和響應內容,參見清單 11。
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」 識別的新聯繫人的示例。
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」 識別的聯繫人(已上一個示例中建立)的最後一個示例而後刪除該聯繫人。
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 | HTTP |
學習
得到產品和技術