文档章节

Java Jersey使用總結

空谷幽兰_
 空谷幽兰_
发布于 2013/08/14 10:26
字数 2172
阅读 16224
收藏 35

关于作者

  • 马隆博(Lenbo Ma),Java,Javascript
  • Blog: http://mlongbo.com
  • E-Mail:mlongbo at gmail.com
  • 创建于:2013/07/26

转载请注明出处:

http://mlongbo.com/2015/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注释获取ServletConfigServletContextHttpServletRequestHttpServletResponseHttpHeaders等,如下:

@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拥有浓厚的兴趣,欢迎与我联系并一起探讨技术,愿共同进步!


扫描二维码,关注我。 内容大多会是后端技术、前端工程、DevOps,偶尔会有一些大数据相关,会推荐一些好玩的东西。希望你会喜欢~

扫描二维码关注我

© 著作权归作者所有

共有 人打赏支持
空谷幽兰_
粉丝 23
博文 26
码字总数 17072
作品 0
海淀
程序员
私信 提问
加载中

评论(13)

_Leo_
_Leo_
你好,如果我需要返回一个404 状态和json消息,怎么做呢?
空谷幽兰_
空谷幽兰_

引用来自“剑龙刀客”的评论

请问我前台有一个表单元素填写的是日期格式的字符串,怎么通过@BeanParam注解转为java.util.Date对象?
直接为Date类型字段加@FormParam字段不行?
剑龙刀客
请问我前台有一个表单元素填写的是日期格式的字符串,怎么通过@BeanParam注解转为java.util.Date对象?
奥西里斯
奥西里斯
楼主,我用Application的话,注册filter,怎么配置啊?Interceptor和filter类似么?
沙发迪
沙发迪
好东西,解决了我很多问题。
沧海龙啸
沧海龙啸
是的,那个问题已经搞定了。现在有个新问题,为什么org.glassfish.jersey.media.multipart这个包不在Jersey2.4.1的jar包里?我把Jersey官网上的bundle全部加到工程里了,但是没有这个包。结果Multipart用不成。
空谷幽兰_
空谷幽兰_

引用来自“沧海龙啸”的评论

引用来自“gfdsgfdstr”的评论

引用来自“沧海龙啸”的评论

我用的服务器Liberty

没用过Liberty,报什么错?

[警告 ] SRVE0274W: 为路径-->/*、包装器-->ServletWrapper[JAX-RS Servlet:[/*]]、应用程序-->Copy of Cloud_Modeler 添加 servlet 映射时出错。
后来我由查看了下,Description  Path  Resource  Location  Type
The servlet class 'com.sun.jersey.spi.container.servlet.ServletContainer' cannot be resolved  /11/WebContent/WEB-INF  web.xml  Servlet: Jersey Web Application  Web Problem
从这句看,貌似是ServletContainer缺少了依赖项,所以没法解析,进而导致了前边的映射错误。

你引入的ServletContainer包不对,com.sun.jersey是2.0以前的包
沧海龙啸
沧海龙啸

引用来自“gfdsgfdstr”的评论

引用来自“沧海龙啸”的评论

我用的服务器Liberty

没用过Liberty,报什么错?

[警告 ] SRVE0274W: 为路径-->/*、包装器-->ServletWrapper[JAX-RS Servlet:[/*]]、应用程序-->Copy of Cloud_Modeler 添加 servlet 映射时出错。
后来我由查看了下,Description  Path  Resource  Location  Type
The servlet class 'com.sun.jersey.spi.container.servlet.ServletContainer' cannot be resolved  /11/WebContent/WEB-INF  web.xml  Servlet: Jersey Web Application  Web Problem
从这句看,貌似是ServletContainer缺少了依赖项,所以没法解析,进而导致了前边的映射错误。
空谷幽兰_
空谷幽兰_

引用来自“沧海龙啸”的评论

我用的服务器Liberty

没用过Liberty,报什么错?
沧海龙啸
沧海龙啸
我用的服务器Liberty
Jersey 2.x 分支 Java SE 兼容性

直到 Jersey 2.6 版本,Jersey 将会兼容 Java SE 6。这个情况将会在在 Jersey 2.7 的版本有所改变。 直到 Jersey 版本 2.25x, 所有的 Jersey 的组件将会兼容 Java SE 7 为目标。这个意思是你...

honeymose
11/06
0
0
【用jersey构建REST服务】系列文章

1.用Jersey构建RESTful服务1--HelloWorldhttp://www.waylau.com/jersey-restful-helloworld/2.用Jersey构建RESTful服务2--JAVA对象转成XML输出http://www.waylau.com/jersey-restful-java-x......

waylau
2014/08/23
0
1
認識JavaFX

作者:蔡學鏞 注意:本文章內容是依據alpha版技術做描述,讀者閱讀時可能已經和實際現況有所差異。 雖 然Ajax方興未艾,但RIA(Rich Internet/Interface Application)也已經揭開序幕,同樣是...

红薯
2009/02/12
827
2
借《淺談PHP與Java之Web開發整合技術》说LAJP

这是早先在网上流传的一篇PHP与Java相结合的技术文章,其中列举了三种整合技术:SOAP、 Quercus、PHP/Java Bridge,这对理解并使用LAJP框架有很好的参考作用,因此将原文摘录在此,并在后面作...

刁忆飞
2010/06/03
0
0
JRuby性能再获提升,使用JRuby实现RESTful服务

JRuby核心开发人员Charles Nutter(headius)近日在其博客中连续发表了两篇文章,介绍了最近他所做的一些有意思的事情:让JRuby更快地运行,以及如何用JRuby 实现RESTful服务。 得益于JVM自身...

红薯
2010/06/08
453
0

没有更多内容

加载失败,请刷新页面

加载更多

linux脚本中父shell与子shell 执行的几种方式

本文主要介绍以下几个命令的区别: shell subshell source $ (commond) `commond` Linux执行Scripts有两种方式,主要区别在于是否建立subshell 1. source filename or . filename 不创建sub...

问题终结者
16分钟前
1
0
安装jdk和Tomcat

12月12日任务 16.1 Tomcat介绍 16.2 安装jdk 16.3 安装Tomcat Tomcat介绍 Tomcat是apache软件基金会(Apache Software Foundation)的Jakarta项目中的一个核心项目,由apache、Sun和其他一些...

robertt15
17分钟前
3
0
Beetl 免费视频

来自 https://my.oschina.net/gking?q=Beetl ,Beetl终于有人录制视频了 项目git地址:https://gitee.com/gavink/beetl-blog 视频地址:下载下来会更清晰,视频比较长,可使用倍速看 百度网盘...

闲大赋
29分钟前
0
0
isEmpty和null的区别

isEmpty和null的区别: 1.一个是对象为空(IsNull),一个是值为空(IsEmpty) 2.IsNull指任务类型变量是否为空包括对象类型的变量。 IsNull函数: 功能:返回Boolean的值,指明表达是否不包...

DemonsI
56分钟前
3
0
Centos7 安装mysql与php

https://blog.csdn.net/qq_36431213/article/details/79576025 官网下载安装mysql-server 依次使用下面三个命令安装 wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.r......

Yao--靠自己
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部