Jersey入门

Rest

REST全称是Representational State Transfer,中文:表述性状态转移。啥意思?不解释。

简单来说:REST使用Web地址(URI)访问资源,使用动词(HTTP请求)操作资源

动词

所谓动词,其实就是HTTP请求。可以将REST理解成一种Web服务协议。REST是直接依赖底层HTTP协议功能的:请求方法、URI地址和响应代码。我们知道HTTP协议是无状态的,同样REST也是无状态的。

动词 对资源的操作 对应DB操作 操作类型
GET 提供资源的只读访问 SELECT 只读
POST 用于更新现有资源或者创建一个新资源 UPDATE N/A
PUT 用于创建一个新资源 CREATE 幂等
DELETE 用于移除一个资源 DELETE 幂等
OPTIONS 用于获取资源上支持的操作   只读
HEAD 只返回HTTP header,不返回 HTTP body   只读

Jersey

之前介绍Rest时,是用Spring + JAXB实现的。生产中,用Jersey更方便。Jersey 是一个开源的、产品级别的JAX-RS API实现框架。

依赖

使用中添加以下依赖即可:

	<dependencies>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
		</dependency>
		<dependency>
			<groupId>org.glassfish.jersey.containers</groupId>
			<artifactId>jersey-container-servlet</artifactId>
			<version>${jersey.version}</version>
		</dependency>
		<dependency>
			<groupId>org.glassfish.jersey.containers</groupId>
			<artifactId>jersey-container-servlet-core</artifactId>
			<version>${jersey.version}</version>
		</dependency>
		<dependency>
			<groupId>org.glassfish.jersey.media</groupId>
			<artifactId>jersey-media-json-jackson</artifactId>
			<version>${jersey.version}</version>
		</dependency>
	</dependencies>

	<properties>
		<jersey.version>2.6</jersey.version>
	</properties>

这里用的是Jersey 2.

Spring boot

因为Spring boot搭建服务器简单,这里使用Spring boot介绍。在Spring boot项目中集成Jersey很简单。

加入依赖

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jersey</artifactId>
		</dependency>

定义资源

@Component
@Singleton
@Path("/resource")
public class MyResource {

    @GET
    public String hello() {
        return "Hello World";
    }
}

注册Resource

在@SpringBootApplication 注解的类中进行注册

	@Bean
    public ResourceConfig resourceConfig() {
        ResourceConfig config = new ResourceConfig();
        config.register(MyResource.class);
        return config;
    }

启动后即可访问我们的Restful了。http://localhost:8080/restful/application.wadl

测试

测试中需要Call我们的Restful,这里我用jersey-client包,加入以下依赖

		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-client</artifactId>
			<version>1.18</version>
		</dependency>
		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-grizzly2</artifactId>
			<version>1.18</version>
		</dependency>
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJsonProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.ws.rs.core.Cookie;

import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertThat;


@RunWith(SpringRunner.class)
@SpringBootTest
public class KakaApplicationTests {

    @Test
    public void contextLoads() {
    }


    @Test
    public void getLightWeightShipment() {
        Client client = Client.create(new DefaultClientConfig());
        WebResource service = client.resource("http://localhost:8180/restful/lookup/tryCookie");
        Cookie cookie = new Cookie("sessionId", "mysession");

        String s1 = service.cookie(cookie).get(String.class);

        assertThat(s1, containsString("mysession"));
    }
}

Resource

定义Resource类,其实就是定义我们的资源,即URI。实际使用中,要定义好的URI,就有Root Resource、Sub-resources之分。

Root Resource

根资源类:即用@Path注释的POJOs(普通的旧Java对象)。其实就是@Path 注解了类,然后为类中方法使用@GET、@PUT、@POST、@DELETE定义。上边的MyResource 就是一个根资源类。

@Component
@Singleton
@Path("/resource")
public class MyResource {

    @GET
    @Produces("text/plain")
    public String getHello() {
        return "Hello World";
    }

    @GET
    @Produces("text/html")
    public String getHelloPage() {
        return "<!DOCTYPE html><html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><title>hello</title></head><body><h2>Hello World!</h2></body></html>";
    }

    @POST
    @Produces("text/plain")
    @Consumes({"text/plain", "application/xml"})
    public String appendHello(@HeaderParam("User-Agent") String name) {
        return "Hello World : " + name;
    }
}

这里我用的是@Singleton 维持一个单例即可。类似的注解还有@RequestScoped和@PerLookup。

注解 作用域 解释
@RequestScoped Request scope 不加注解默认使用该作用域。
系统会为每个匹配资源类URI的请求创建一个实例
@PerLookup Per-lookup scope 每个session上创建一个实例
@Singleton Singleton 应用范围内,只会创建资源类的一个实例

这里也根据HTTP请求的数据类型不同,提供不同的响应。

注解 解释 对应HTTP字段 例子
@Consumes 指定http响应的MIME类型
数组类型
默认*/*,表示任意的MIME类型
Content-Type @Consumes(MediaType.TEXT_PLAIN)
@Consumes({"text/plain", "application/xml"})
@Consumes("application/x-www-form-urlencoded")
@Produces 指定http请求的MIME类型
数组类型
默认是*/*,表示任意的MIME类型
Accept @Produces("text/plain")
@Produces({"application/xml", "application/json"})

Sub-resources

@Path也可以被用在根资源类的方法上。此时每个被注解的方法就是一个子资源。

@Path("/lookup")
public class LookupResource {

    @GET
    @Path("/info")
    @Produces({"application/json", "application/xml", MediaType.TEXT_PLAIN})
    public PersonInfo getInfo() {
        PersonInfo info = new PersonInfo();
        info.setName("Hustzw");
        info.setAge(20);
        info.setPhoneNo("12346789");
        return info;
    }

    @GET
    @Path("/list")
    @Produces({"application/json", "application/xml", MediaType.TEXT_PLAIN})
    public List<PersonInfo> getAll() {
        List<PersonInfo> list = new ArrayList<>();
        PersonInfo info = new PersonInfo();
        info.setName("Hustzw");
        info.setAge(20);
        info.setPhoneNo("12346789");
        list.add(info);

        PersonInfo info2 = new PersonInfo();
        info.setName("Kaka");
        info.setAge(21);
        info.setPhoneNo("9875645");
        list.add(info);
        return list;
    }
}

参数注解

Jersey提供了一些@*Param 形式的注解可以帮我们将请求的参数自动解析。

注解 作用 例子
@PathParam 配合@Path来使用的,使用方式在@Path用{}来指定路径中匹配的参数 @Path("/info/{id}")
参数中:@PathParam("id") String userId
QueryParam 获取url中拼接在?后面的参数 URI:x?step=3
参数:@QueryParam("step") int step
FormParam 客户端使用form(MIME为application/x-www-form-urlencoded)的方式提交表单
服务端使用@FormParam解析form表单中的参数
@Consumes("application/x-www-form-urlencoded")
?name=zhangwei&age=20
@FormParam("name") String name, @FormParam("age") int age
FormDataParam 上传文件的时候,需要@FormDataParam
客户端提交form(MIME为multipart/form-data)的方式提交表单
服务端使用@FormDataParam来解析form表单中的参数
 
HeaderParam 获取http请求头中的参数值 @HeaderParam("User-Agent") String name
CookieParam 获取http请求头中cookie中的参数值  
MatrixParam URL中“;”作为分隔符键值对。可以接受List参数,即相同key的都被解析成List  
DefaultValue

配合@PathParam、@QueryParam、@FormParam、@FormDataParam、@MatrixParam、

@HeaderParam、@CookieParam等使用.

请求指定的参数中没有值时,就使用@DefaultValue中的值为默认值
解析出错会404
 
BeanParam 将请求参数解析成Bean。

Bean中的字段使用@PathParam、@QueryParam、@FormParam、@FormDataParam、

@MatrixParam、@HeaderParam、@CookieParam等来注解

 
Context 用来注入。算是对Servlet的一种封装。
注入UriInfo、HttpHeader、ServletConfig、ServletContext、HttpServletRequest、HttpServletResponse等
 
Encoded    

看代码

@PathParam

@Path("/lookup")
public class LookupResource {

    @GET
    @Path("/info/{id}")
    @Produces({"application/json", "application/xml", MediaType.TEXT_PLAIN})
    public PersonInfo getInfo(@PathParam("id") String id) {
        PersonInfo info = new PersonInfo();
        info.setId(id);
        info.setName("Hustzw");
        return info;
    }
}

@PathParam会把@Path("{}")中大括号里面的值解析。访问http://localhost:8180/restful/lookup/info/2,此时发现 2 就被解析成id。

@QueryParam

    @GET
    @Path("/list")
    @Produces({"application/json", "application/xml", MediaType.TEXT_PLAIN})
    public List<PersonInfo> getAll(@QueryParam("name") String name) {// lookup/list?name=zw
        List<PersonInfo> list = new ArrayList<>();
        // ...
        return list;
    }

@QueryParam把URI里面?之后的参数进行解析,比如,http://localhost:8180/restful/lookup/list?name=zw

@FormParam

    @POST
    @Path("/update")
    @Consumes("application/x-www-form-urlencoded")// lookup/list?name=zw&age=20
    @Produces({MediaType.TEXT_PLAIN})
    public String update(@NotNull @FormParam("name") String name, @DefaultValue("26") @NotNull @FormParam("age") int age) {
        return "success " + name + " "+ age;
    }

测试时注意这里我用的是POST。使用该注解时要指定是form的提交("application/x-www-form-urlencoded")。访问的URI被解析成form。http://localhost:8180/restful/lookup/update?name=zw&age=20

@FormDataParam

用于上传文件:

@HeaderParam

    @GET
    @Path("/hello")
    @Produces("text/plain")
    @Consumes({"text/plain", "application/xml"})
    public String appendHello(@HeaderParam("User-Agent") String name) {
        return "Hello World : " + name;
    }

用GET请求,对HTTP header的"User-Agent"解析成参数。http://localhost:8180/restful/lookup/hello

@CookieParam

    @GET
    @Path("/tryCookie")
    public String checkCookie(@CookieParam("sessionId") String sessionId) {
        return "Hello World : " + sessionId;
    }

cookie的Client如下:

    @Test
    public void getLightWeightShipment() {
        Client client = Client.create(new DefaultClientConfig());
        WebResource service = client.resource("http://localhost:8080/restful/lookup/tryCookie");
        Cookie cookie = new Cookie("sessionId", "mysession");

        String s1 = service.cookie(cookie).get(String.class);

        assertThat(s1, containsString("mysession"));
    }

@MatrixParam

给上边的类加入一个新属性

@XmlRootElement
public class PersonInfo implements Serializable {
    // ...
    @MatrixParam("alias")
    private List<String> alias;
}

@BeanParam

然后提供访问方法:

    @POST
    @Path("/multiple")
    public String checkAlias(@BeanParam PersonInfo info) {
        return "Hello World : " + info.getAlias().get(0);
    }

此时访问该资源时,URI需要用分号 ; 而不是 & 。另外,参数再URI中,因此PersonInfo中,字段上是要用@QueryParam。如

http://localhost:8080/restful/lookup/multiple;id=2;alias=zw;alias=kaka;alias=hust

@Context

在使用Rest时,我们可能需要一些HttpServletRequest 或者 URI的一些信息。Jersey帮我们也提供了注入接口。

    @GET
    @Path("/viewpath")
    public String checkURI(@Context UriInfo ui) {
        return "Hello World : " + ui.getPath();
    }

上边是注入给方法,当然也可以把这些实例注入给当前resource的实例:

@Path("/lookup")
public class LookupResource {

    @Context
    HttpServletRequest req;

    @Context
    ServletConfig servletConfig;

    @Context
    ServletContext servletContext;
    // ...
}

支持JSON

Web Service在使用时,除了XML外更倾向用JSON数据,它更传递数据时,体积更小。Jersey提供了对JSON的支持,请求内容为JSON。官网给出了三种方式。不过我在用的时候,发现解析不了。也没找到原因。

    @POST
    @Path("/place")
    @Consumes(MediaType.APPLICATION_JSON)
    public String place(@BeanParam DetailPlace country) {
        return "country " + country.getArea();
    }

然后register feature

@Component
@ApplicationPath("restful")
public class JerseyConfig extends ResourceConfig {
    public JerseyConfig() {
        register(JacksonFeature.class);

        register(MyResource.class);
        register(ExampleResource.class);
    }
}

对应的Bean

@JsonInclude(JsonInclude.Include.NON_DEFAULT)
public class DetailPlace {

    @QueryParam("region")
    private String region;

    @QueryParam("area")
    private String area;

    public DetailPlace() {
    }

    public DetailPlace(String region, String area) {
        this.region = region;
        this.area = area;
    }
}

然后Client call service

    @Test
    public void testResources() {
        ClientConfig cc = new DefaultClientConfig();
        Client client = Client.create(cc);
        WebResource service = client.resource("http://localhost:8080/restful/example/place");

        String result = service.header("Content-Type", "application/json").
                post(String.class, "{'region':'Asia','area':'976'}");

        assertThat(result, containsString("Asia"));
    }

结果测试失败!还在学习

Jersey 1 和 2的区别

猜你喜欢

转载自blog.csdn.net/hustzw07/article/details/80878486