Java Jersey2使用總結


前言

在短信平台一期工作中,為便於移動平台的開發,使用了Java Jersey框架開發RESTFul風格的Web Service接口。在使用的過程中發現了一些問題並積累了一些項目經驗,做了一下總結,便於個人成長,同時也希望對有需要的同仁有好的借鑒和幫助。

簡介

Jersey是JAX-RS(JSR311)開源參考實現用於構建 RESTful Web service,它包含三個部分:

  • 核心服務器(Core Server) :通過提供JSR 311中標准化的注釋和API標准化,可以用直觀的方式開發RESTful Web服務。

  • 核心客戶端(Core Client) :Jersey客戶端API能夠幫助開發者與RESTful服務輕松通信;

  • 集成(Integration) :Jersey還提供可以輕松繼承Spring、Guice、Apache Abdera的庫。

在本次開發中使用Jersey2.0,並且僅使用了核心服務器。

設置Jersey環境

Maven

<!--jersey--> <dependency>  <groupId>org.glassfish.jersey.containers</groupId>  <artifactId>jersey-container-servlet-core</artifactId>  <version>2.0</version> </dependency> <!--JAXB API--> <dependency>  <groupId>javax.xml.ws</groupId>  <artifactId>jaxws-api</artifactId>  <version>2.1</version> </dependency> <!-- Json支持 --> <dependency>  <groupId>org.codehaus.jackson</groupId>  <artifactId>jackson-core-asl</artifactId>  <version>1.9.12</version> </dependency> <dependency>  <groupId>org.codehaus.jackson</groupId>  <artifactId>jackson-mapper-asl</artifactId>  <version>1.9.12</version> </dependency> <dependency>  <groupId>org.codehaus.jackson</groupId>  <artifactId>jackson-jaxrs</artifactId>  <version>1.9.12</version> </dependency> 

引入Jar文件方式

從Jersey開發包中將以下庫復制的WEB-INF下的庫目錄:

  • 服務器:jersey-server.jar 、jersey-container-servlet-core.jar、jersey-container-servlet.jar、javax.ws.rs-api-2.0.jar

  • 客戶端:jersey-client.jar

  • common:jersey-common.jar

  • json支持:在Jersey2.0中需要使用 Jackson1.9 才能支持json。

Hello World

以下將展示一個Hello World

第一步: 編寫一個名為HelloResource的資源,它接受Http Get請求並響應“Hello Jersey”

@Path("/hello") public class HelloResource {  @GET  @Produces(MediaType.TEXT_PLAIN)  public String sayHello() {   return "Hello Jersey";  } } 

第二步: 編寫JAX-RS application

public class APIApplication extends ResourceConfig { public APIApplication() {  //加載Resource  register(HelloResource.class);  //注冊數據轉換器  register(JacksonJsonProvider.class);  // Logging.  register(LoggingFilter.class); } } 

第三步: 在web.xml文件中定義servelt調度程序,目的是將所有REST請求發送到Jersey容器。除了聲明Jersey Servlet外,還需定義一個初始化參數,指定JAX-RS application。

<!--用於定義 RESTful Web Service 接口--> <servlet>  <servlet-name>JerseyServlet</servlet-name>  <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>  <init-param>   <param-name>javax.ws.rs.Application</param-name>   <param-value>cn.com.mink.resource.APIApplication</param-value>  </init-param>  <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping>  <servlet-name>JerseyServlet</servlet-name>  <url-pattern>/services/*</url-pattern> </servlet-mapping> 

第四步: 測試程序

在命令終端中輸入以下命令,將會看到“Hello Jersey”。

 

curl http://host:port/services/hello

 

或者在瀏覽器中輸入以下URL,將會看到“Hello Jersey”

 

http://host:port/services/hello

 

使用

資源

Root Resource And Sub-Resource

資源是組成RESTful服務的關鍵部分,可以使用HTTP方法(如:GET、POST、PUT和DELETE)操作資源。在JAX-RX中,資源通過POJO實現,使用 @Path 注釋組成其標識符。資源可以有子資源,父資源是資源集合,子資源是成員資源。

在以下樣例代碼中,

Resources是"/services" URI組成是集合資源,UserResource是“/services/user” URI組成的成員資源;

@Path("/services") public class Resources {  @Path("/user")  public UserResource getUserResource() {   ...  }  @Path("/book")  public BookResource getBookResource() {   ...  } } 

UserResource是“/user” URI組成的集合資源,getUser是“/user/{username}” URI組成的資源方法

@Path("/user") public class UserResource {  @GET  @Path("{username"})  @Produces("application/json")  public User getUser(@PathParam("username") String userName) {   ...  } } 

HTTP Methods

HTTP方法映射到資源的CRUD(創建、讀取、更新和刪除)操作,基本模式如下:

  • HTTP GET :讀取/列出/檢索單個或資源集合。
  • HTTP POST :新建資源。
  • HTTP PUT :更新現有資源或資源集合。
  • HTTP DELETE :刪除資源或資源集合。

@Produces

@Produces 注釋用來指定將要返回給client端的數據標識類型(MIME)。@Produces 可以作為class注釋,也可以作為方法注釋,方法的 @Produces 注釋將會覆蓋class的注釋。

  • 指定一個MIME類型

     

    @Produces("application/json")

     

  • 指定多個MIME類型

     

    @Produces({"application/json","application/xml"})

     

@Consumes

@Consumes 與 @Produces 相反,用來指定可以接受client發送過來的MIME類型,同樣可以用於class或者method,也可以指定多個MIME類型,一般用於 @PUT ,@POST 。

參數(Parameter Annotations)

Parameter Annotations用於獲取client發送的數據。本文只介紹常用的注解,更多詳見 Jersey用戶手冊

@PathParam

使用 @PathParam 可以獲取URI中指定規則的參數,比如:

@GET @Path("{username"}) @Produces(MediaType.APPLICATION_JSON) public User getUser(@PathParam("username") String userName) { ... } 

當瀏覽器請求 http://localhost/user/jack 時,userName值為jack。

@QueryParam

@QueryParam 用於獲取GET請求中的查詢參數,如:

@GET @Path("/user") @Produces("text/plain") public User getUser(@QueryParam("name") String name, @QueryParam("age") int age) { ... } 

當瀏覽器請求 http://host:port/user?name=rose&age=25 時,name值為rose,age值為25。如果需要為參數設置默認值,可以使用 @DefaultValue ,如:

@GET @Path("/user") @Produces("text/plain") public User getUser(@QueryParam("name") String name, @DefaultValue("26") @QueryParam("age") int age) { ... } 

當瀏覽器請求 http://host:port/user?name=rose 時,name值為rose,age值為26。

@FormParam

@FormParam ,顧名思義,從POST請求的表單參數中獲取數據。如:

@POST @Consumes("application/x-www-form-urlencoded") public void post(@FormParam("name") String name) { // Store the message } 

@BeanParam

當請求參數很多時,比如客戶端提交一個修改用戶的PUT請求,請求中包含很多項用戶信息。這時可以用 @BeanParam 。

@POST @Consumes("application/x-www-form-urlencoded") public void update(@BeanParam User user) { // Store the user data } 

User Bean定義如下:

@XmlRootElement(name = "user") public class User {  @PathParam("userName)  private String userName;  @FormParam("name")  private String name;  @FormParam("telephone")  private String telephone;  @FormParam("email")  private String email;  public String getUserName() {   return userName;  }  public void setUserName(String userName) {   this.userName = userName;  }  ... } 

使用Map

在一個大型的server中,因為參數的多變,參數結構的調整都會因為以上幾種方式而遇到問題,這時可以考慮使用 @Context 注釋,並獲取UriInfo實例,如下:

@GET
public String get(@Context UriInfo ui) {
    MultivaluedMap<String, String> queryParams = ui.getQueryParameters(); MultivaluedMap<String, String> pathParams = ui.getPathParameters(); } 

同樣還可以通過 @Context 注釋獲取 ServletConfig 、 ServletContext 、HttpServletRequest 、 HttpServletResponse 和 HttpHeaders 等,如下:

@Path("/") public class Resource {  @Context  HttpServletRequest req;  @Context  ServletConfig servletConfig;  @Context  ServletContext servletContext;  @GET  public String get(@Context HttpHeaders hh) {   MultivaluedMap<String, String> headerParams = hh.getRequestHeaders();   Map<String, Cookie> pathParams = hh.getCookies();  } } 

Jersey返回Json和Xml

JAX-RS支持使用JAXB(Java API for XML Binding)將JavaBean綁定到XML或JSON,反之亦然。JavaBean必須使用 @XmlRootElement 標注,沒有@XmlElement 注釋的字段將包含一個名稱與之相同的XML元素,如下:

@XmlRootElement public class OptionResult {  @XmlElement(name = "code")  private String result;  private String errorMsg;  public String getResult() {   return result;  }  public void setResult(String result) {   this.result = result;  }  public String getErrorMsg() {   return errorMsg;  }  public void setErrorMsg(String errorMsg) {   this.errorMsg = errorMsg;  } } 

然后在REST服務中使用:

@Path("/user") public class UserResource {  @POST  @Produces("application/json")  public OptionResult create(@BeanParam User user) {   ...  } } 

最后,要注冊數據轉換器,該轉換器會自動將JavaBean轉換為json數據:

public class APIApplication extends ResourceConfig { public APIApplication() {  //加載Model  register(OptionResult.class);  //加載與OptionResult同一個packge的Model  //packages(OptionResult.class.getPackage().getName());  //加載Resource  register(UserResource.class);  //注冊數據轉換器  register(JacksonJsonProvider.class);  // Logging.  register(LoggingFilter.class); } } 

說明 :返回XML數據的原理相同,僅僅是數據轉換器不同,只需要在APIApplication中同時注冊XML數據轉換器即可,詳見 Jersey用戶手冊

問題總結

Ajax請求(POST、PUT和DELETE)無法將數據提交到Jersey容器

問題闡述

在短信平台的開發中,數據的CRUD全部使用Ajax技術完成,因此必須使用POST、PUT和DELETE請求。此三種請求的content-type均為“application/x-www-form-urlencoded”,使用UTF-8編碼會變成“application/x-www-form-urlencoded; UTF-8”。在使用Firefox的tamperdata擴展調試程序的過程中發現,當content-type為“application/x-www-form-urlencoded”時,Jersey容器能夠通過 @FormParam 注解獲取到提交的數據,而content-type為“application/x-www-form-urlencoded; UTF-8”時便獲取不到。

解決方案

最終我使用Java Filter和Jersey RequestFilter解決了問題。首先在Java Filter中使用UTF8將Request中的數據編碼,然后在Jersey RequestFilter中將request對象中的content-type修改為“application/x-www-form-urlencoded”。如:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {  HttpServletRequest req = (HttpServletRequest)request;  req.setCharacterEncoding("UTF-8"); } public class RequestFilter implements ContainerRequestFilter {  @Override  public void filter(ContainerRequestContext context) throws IOException {   String headerString = context.getHeaderString("content-type");   if (headerString != null) {    //如果content-type以"application/x-www-form-urlencoded"開頭,則處理   if (headerString.startsWith(MediaType.APPLICATION_FORM_URLENCODED))     context.getHeaders().putSingle("content-type", MediaType.APPLICATION_FORM_URLENCODED);   }  } } 

最后在web.xml中注冊Java Filter(要注冊在Jersey容器之前),在APIApplication中注冊Jersey RequestFilter,如下:

public class APIApplication extends ResourceConfig { public APIApplication() { register(RequestFilter.class); } } 

說明 :在修復此問題后,在Github的Jersey源代碼倉庫中看到已經有人發現並修復了此問題,在下個Jersey正式版本中應該不會再出現這樣的問題,詳見 此Discussion

后記

本人在使用Jersey的過程中發現網上有關Jersey的中文資料並不多,因此將本期開發中的使用經驗總結於此,便於同樣對Jersey感興趣的同仁參考。如果你也有Jersey的開發經驗並且對Jersey擁有濃厚的興趣,歡迎與我聯系並一起探討技術,願共同進步!

該文檔創建於:2013/7/26

轉載請注明出處.

作者 :馬隆博

E-Mail :mlongbo@gmail.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM