Java技巧:用 Java 技術建立 RESTful Web 服務/@Path@Produces@PathParam
簡介
JAX-RS (JSR-311) 是為 Java EE 環境下的 RESTful 服務能力提供的一種規範。它能提供對傳統的基於 SOAP 的 Web 服務的一種可行替代。
在本文中,瞭解 JAX-RS 的主要組件。本文用一個例子展示了一個企業如何使用 JAX-RS 內的功能以一種 Restful 的方式公開員工的聯絡資訊。
背景
多年來,開發人員使用各種工具在其 Java 應用程式內建立 RESTful 服務。由於 REST 架構的簡單性,主要需求 — 接收 HTTP 訊息和頭部的能力 — 可以由一個簡單的 Java Web 容器實現。
Java servlets 常被用來開發 RESTful 應用程式。如何使用 servlet 並沒有固定的模式。通常,servlet 會接受請求並自己解析這個 HTTP 要求 URI,以將此請求與一個已知資源相匹配。對於 REST 服務開發,這個簡單的 servlet 模型以更為正式的 API 得到擴充。但是,因為這些 API 是在 servlet 模型之上開發的,所以這些 API 中沒有一個是作為正式的標準開發的。
隨著 REST 越來越多地被採用為一種架構,Java Community Process (JCP) 計劃在未來的 Java Enterprise Edition 6 發布版中包括對 REST 的正式支援。JSR-311 也已建立好,並已有了 JAX-RS 1.0 規範,提供了一種新的基於注釋的方式來開發 RESTful 服務。與 servlet 模型相比,JAX-RS 注釋讓您能集中於您的資源和資料對象。並且,您不必再開發通訊層(通過 servlet)。
Java 資源
JAX-RS 建立了一種特殊的語言來描述資源,正如由其編程模型所表示的。有五種主要條目:根資源、子資源、資源方法、子資源方法以及子資源定位器。
根資源
根資源是由 @Path 注釋的 Java 類。@Path 注釋提供了一個 value 屬性,用來表明此資源所在的路徑。value 屬性可以是文本字元、變數或變數外加一個定製的Regex。清單 1 給出了一個例子。
清單 1. JAX-RS 根資源
package com.ibm.jaxrs.sample.organization;import javax.ws.rs.Path;@Path(value="/contacts")public class ContactsResource {...} |
子資源
子資源是作為 subresource locator 調用的結果返回的 Java 類。它們類似於根資源,只不過它們不是由 @Path 注釋的,因它們的路徑是由子資源定位器給出的。子資源通常包含由 HTTP 要求方法指示符(designator)注釋的方法以便服務此請求。如果它們不包含如此注釋的方法,那麼它們將會通過指派給合適的子資源定位器來進一步解析此資源處理請求。
清單 2. JAX-RS 子資源
package com.ibm.jaxrs.sample.organization;import javax.ws.rs.GET;public class Department {@GETpublic String getDepartmentName() {...}} |
如上所示的清單 2 展示了由 ContactsResource.getContactDepartment 方法返回的子資源。在這個例子中,如果一個 HTTP GET 請求被發送給 /contact/{contactName}/department 路徑,那麼 Department 子資源內的 getDepartmentName 資源方法就會處理此請求。
資源方法
資源方法是根資源或子資源內綁定到 HTTP 方法的 Java 方法。綁定是通過諸如 @GET 這樣的注釋完成的。
清單 3. JAX-RS 資源方法
package com.ibm.jaxrs.sample.organization;import java.util.List;import javax.ws.rs.GET;import javax.ws.rs.Path;@Path(value="/contacts")public class ContactsResource {@GETpublic List<ContactInfo> getContacts() {...}} |
在清單 3 的例子中,發送到 /contacts 路徑的 HTTP GET 請求將會由 getContacts() 資源方法處理。
子資源方法
子資源方法非常類似於資源方法;惟一的區別是子資源方法也是由 @Path 注釋的,此注釋進一步限定了該方法的選擇。
清單 4. JAX-RS 子資源方法
package com.ibm.jaxrs.sample.organization;import java.util.List;import javax.ws.rs.GET;import javax.ws.rs.Path;@Path(value="/contacts")public class ContactsResource {@GETpublic List<ContactInfo> getContacts() {...}@GET@Path(value="/ids")public List<String> getContactIds() {...}} |
在清單 4 中,發送到 /contacts/ids 路徑的 HTTP GET 請求將會由 getContactIds() 子資源方法處理。
子資源定位器
子資源定位器是能進一步解析用來處理給定請求的資源的一些方法。它們非常類似於子資源方法,因它們具備一個 @Path 注釋,但不具備 HTTP 要求方法指示符,比如 @GET 注釋。
清單 5. JAX-RS 子資源定位器
package com.ibm.jaxrs.sample.organization;import java.util.List;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.PathParam;@Path(value="/contacts")public class ContactsResource {@GETpublic List<ContactInfo> getContactss() {...}@GET@Path(value="/ids")public List<String> getContactIds() {...}@Path(value="/contact/{contactName}/department")public Department getContactDepartment(@PathParam(value="contactName") String contactName) {...}} |
在上述例子中,對 /contact/{contactName}/department 路徑的任何 HTTP 要求都將由 getContactDepartment 子資源定位器處理。 {contactName} 部分表明 contact 路徑部分之後可以是任何合法的 URL 值。
注釋
本節將會探討一些重要的注釋及其使用。對於由 JAX-RS 規範提供的注釋的完整列表,可以參考本文的 參考資料 部分給出的 JSR-311 連結。
@Path
@Path 注釋被用來描述根資源、子資源方法或子資源的位置。value 值可以包含文本字元、變數或具有定製Regex的變數。清單 6 的例子展示了 @Path 注釋的主要應用。
清單 6. @Path 的使用
package com.ibm.jaxrs.sample.organization;import java.util.List;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.PathParam;@Path(value="/contacts")public class ContactsResource {@GET@Path(value="/{emailAddress:.+@.+\\.[a-z]+}")public ContactInfo getByEmailAddress(@PathParam(value="emailAddress") String emailAddress) {...}@GET@Path(value="/{lastName}")public ContactInfo getByLastName(@PathParam(value="lastName") String lastName) {...}} |
ContactsResource 類上的注釋表明對 /contacts 路徑的所有請求都將由 ContactsResource 根資源處理。getByEmailAddress 上的 @Path 注釋則表明任何發送到 /contacts/{emailAddress} 的請求(其中 emailAddress 代表的是Regex .+@.+\\.[a-z]+)都將由 getByEmailAddress 處理。
getByLastName 方法上的 @Path 注釋指定了發送到 /contacts/{lastName} 路徑的所有請求(其中 lastName 代表的是一個與getByEmailAddress 內的Regex不匹配的有效 URL 部分)都將由 getByLastName 方法處理。
@GET、@POST、@PUT、@DELETE、@HEAD
@GET、@POST、@PUT、@DELETE 以及 @HEAD 均是 HTTP 要求方法指示符注釋。您可以使用它們來綁定根資源或子資源內的 Java 方法與 HTTP 要求方法。HTTP GET 請求被映射到由 @GET 注釋的方法;HTTP POST 請求被映射到由 @POST 注釋的方法,以此類推。使用者可能還需要通過使用 @HttpMethod 注釋定義其自己的定製 HTTP 要求方法指示符。
清單 7. 定製的 HTTP 要求方法指示符注釋
package com.ibm.jaxrs.sample.organization;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import javax.ws.rs.HttpMethod;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@HttpMethod("GET")public @interface CustomGET {} |
上述的聲明定義了 @CustomGET 注釋。此注釋將具有與 @GET 注釋相同的語義值並可用在其位置上。
@Conumes 和 @Produces
@Consumes 注釋代表的是一個資源可以接受的 MIME 類型。@Produces 注釋代表的是一個資源可以返回的 MIME 類型。這些注釋均可在資源、資源方法、子資源方法、子資源定位器或子資源內找到。
清單 8. @Consumes/@Produces
package com.ibm.jaxrs.sample.organization;import java.util.List;import javax.ws.rs.Consumes;import javax.ws.rs.GET;import javax.ws.rs.Path;import javax.ws.rs.PathParam;import javax.ws.rs.Produces;@Path(value="/contacts")public class ContactsResource {@GET@Path(value="/{emailAddress:.+@.+\\.[a-z]+}")@Produces(value={"text/xml", "application/json"})public ContactInfo getByEmailAddress(@PathParam(value="emailAddress") String emailAddress) {...}@GET@Path(value="/{lastName}")@Produces(value="text/xml")public ContactInfo getByLastName(@PathParam(value="lastName") String lastName) {...}@POST@Consumes(value={"text/xml", "application/json"})public void addContactInfo(ContactInfo contactInfo) {...}} |
對於上述的 getByEmailAddress 和 addContactInfo 方法,它們均能處理 text/xml 和 application/json。被接受或返回的資源表示將依賴於客戶機設定的 HTTP 要求頭。@Consumes 注釋針對 Content-Type 要求標頭進行匹配,以決定方法是否能接受給定請求的內容。
在清單 9 中,application/json 的 Content-Type 頭再加上對路徑 /contacts 的 POST,表明我們的 ContactsResource 類內的 addContactInfo 方法將會被調用以處理請求。
清單 9. Content-Type 頭部的使用
POST /contacts HTTP/1.1Content-Type: application/jsonContent-Length: 32 |
相反地,@Produces 注釋被針對 Accept 要求標頭進行匹配以決定客戶機是否能夠處理由給定方法返回的表示。
清單 10. Accept 頭部的使用
GET /contacts/johndoe@us.ibm.com HTTP/1.1Accept: application/json |
在清單 10 中,對 /contacts/johndoe@us.ibm.com 的 GET 請求表明了 getByEmailAddress 方法將會被調用並且返回的格式將會是application/json,而非 text/xml。
Providers
JAX-RS 提供者是一些應用程式組件,允許在三個關鍵領域進行運行時行為的定製:資料繫結、異常映射以及上下文解析(比如,向運行時提供 JAXBContext 執行個體)。每個 JAX-RS 提供者類必須由 @Provider 注釋。如下的例子討論了兩個資料繫結提供者MessageBodyWriter 和 MessageBodyReader。