您的位置:首页 > 其它

Jersey入门到放弃-4

2017-04-12 00:00 204 查看
摘要: JAX-RS相关概念-资源/子资源

1. 资源类

即使用@Path标注以及至少一个方法使用@Path或使用@GET、@POST、@PUT、@DELETE等标注的普通Java类。以下示例展示如何使用Jersey构建Restful Api.

package org.glassfish.jersey.examples.helloworld;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("helloworld")
public class HelloWorldResource {
public static final String CLICHED_MESSAGE = "Hello World!";

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

接下来分析以下上面的示例中的注解。

@Path

用于指定这是一个资源,该注解适用于类和方法,请求路径为 /helloworld, 该注解中的value是一个相对路径。该值不仅可以指定常量,还可以指定变量, 例如:

@Path("/users/{username}")

此时/users/任意value 都可以匹配到, 如果需要在方法中获取该{username}的实际值:则使用@PathParam注解在方法参数值指定,此时就可以在方法中使用了。

@Path("/users/{username}")
public class UserResource {

@GET
@Produces("text/xml")
public String getUser(@PathParam("username") String userName) {

}
}

如果还需要指定传入的{username}符合指定的规则,我们可以使用正则表达式指定,例如只允许包含大小写字母及数字及下划线,并以大小写字母开始, 如果传入参数的不符合该规则就会回复404错误码。

@Path("users/{username: [a-zA-Z][a-zA-Z_0-9]*}")

另外 @Path("helloworld") 、@Path("/helloworld") 、@Path("helloworld/") 、@Path("/helloworld/") Jersey会识别为同一个api。因此加不加/没什么区别

@GET、@POST、@PUT、@DELETE等HTTP方法

这些注解用来标识该方法处理什么类型的请求, 适用于方法,一般情况对应关系如下:

@GET获取资源
@POST保存资源
@PUT修改资源
@DELETE删除资源
还有另外一些注解如@OPTION 、@HEAD不常用,有兴趣的朋友可以自己去了解下。

@Produces

用来指定响应客户端的数据类型(json、xml、text、html等),即回复给客户端的数据类型。适用于类或方法。

@Path("/myResource")
@Produces("text/plain")
public class SomeResource {
@GET
public String doGetAsPlainText() {

}

@GET
@Produces("text/html")
public String doGetAsHtml() {

}
}

doGetAsPlainText()方法指定的响应类型为text/plain,由类上的@Produce注解指定。

doGetAsHtml()方法指定的响应类型为text/html,该方法上的@Produce注解覆盖了类上的@Produce注解。

如果一个资源类支持不止一种响应类型,例如上例, 则客户端收到的响应结果是如何呢?此时由客户端所发送请求的Http header上的 Accept header 决定,

例如:Accept: text/plain时,则由doGetAsPlainText()处理。

Accept: text/plain;q=0.9, text/html,此时客户端text、html都可以接受,该如何决定呢?这是q=0.9就起作用了,代表客户端更期望text/html,则此时doGetAsHtml()被调用。

如果多个响应类型指定在同一个方法上,例如:

@GET
@Produces({"application/xml", "application/json"})
public String doGetAsXmlOrJson() {

}

则此时回复什么类型呢? 与一个资源类支持不止一种响应类型”原理一致。

当客户端接受多种类型时,且具有同样的优先级【即没有指定q=0.9之类的】,则服务端可以指定优先回复某一种类型

@Produces({"application/xml; qs=0.9", "application/json"})


@Comsumes

用来制定api可以接收的参数类型。适用于方法和类,可以指定接受多种类型,如下示例指定接受文本数据,一般情况下不需要指定该注解。

@POST
@Consumes("text/plain")
public void postClichedMessage(String message) {
// Store the message
}

2.参数类型的注解

@PathParam : 获取请求url中的参数(/users/{username}

@QueryParam: 获取request中的请求参数(Get请求,/resource?username=xxx)

@Path("smooth")
@GET
public Response smooth(
@DefaultValue("2") @QueryParam("step") int step,
@DefaultValue("true") @QueryParam("min-m") boolean hasMin,
@DefaultValue("true") @QueryParam("max-m") boolean hasMax,
@DefaultValue("true") @QueryParam("last-m") boolean hasLast,
@DefaultValue("blue") @QueryParam("min-color") ColorParam minColor,
@DefaultValue("green") @QueryParam("max-color") ColorParam maxColor,
@DefaultValue("red") @QueryParam("last-color") ColorParam lastColor) {

}

如果请求的参数中包含step且可以转换为int类型,则将该参数值赋予step,如果不包含step,则step等于默认值2,如果包含但不能转化为int类型,则回复404错误代码到客户端。其他参数类似。

针对上面用户自定义类型的参数ColorParam实现如下:

public class ColorParam extends Color {

public ColorParam(String s) {
super(getRGB(s));
}

private static int getRGB(String s) {
if (s.charAt(0) == '#') {
try {
Color c = Color.decode("0x" + s.substring(1));
return c.getRGB();
} catch (NumberFormatException e) {
throw new WebApplicationException(400);
}
} else {
try {
Field f = Color.class.getField(s);
return ((Color)f.get(null)).getRGB();
} catch (Exception e) {
throw new WebApplicationException(400);
}
}
}
}

一般情况下方法参数的类型可以是:

基本数据类型

构造器接受String参数的类

包含接收String参数的静态方法valueOf/fromString的类

Have a registered implementation of
javax.ws.rs.ext.ParamConverterProvider
JAX-RS extension SPI that returns a
javax.ws.rs.ext.ParamConverter
instance capable of a "from string" conversion for the type. or 【这一点不是很明白,先mark吧】

List<T>
,
Set<T>
or
SortedSet<T>
, T是上面步骤2/3的类型

@MatrixParam: 从URL path segments中获取参数

@HeaderParam: 从Http请求头获取参数

@CookieParam: 从cookie中获取参数

@FormParam:获取POST请求参数

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


我们也可以通过以下方式获取所有的PathParam和QueryParam参数的值

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

针对header和cookie的所有值也可以使用如下代码

@GET
public String get(@Context HttpHeaders hh) {
MultivaluedMap<String, String> headerParams = hh.getRequestHeaders();
Map<String, Cookie> pathParams = hh.getCookies();
}

同样Post过来的所有值也可以使用如下代码

@POST
@Consumes("application/x-www-form-urlencoded")
public void post(MultivaluedMap<String, String> formParams) {
// Store the message
}


@BeanParam

提供了一种将多个请求参数组织到一个java类中的方式

public class MyBeanParam {
@PathParam("p")
private String pathParam;

@MatrixParam("m")
@Encoded
@DefaultValue("default")
private String matrixParam;

@HeaderParam("header")
private String headerParam;

private String queryParam;

public MyBeanParam(@QueryParam("q") String queryParam) {
this.queryParam = queryParam;
}

public String getPathParam() {
return pathParam;
}
}

如何使用该类呢?

@POST
public void post(@BeanParam MyBeanParam beanParam) {
final String pathParam = beanParam.getPathParam(); // contains injected path parameter "p"

}

即使在MyBeanParam中包含了要注入的参数,也可以在需要的时候单独注入,也可以注入多个bean。

@POST
public void post(@BeanParam MyBeanParam beanParam, @BeanParam AnotherBean anotherBean, @PathParam("p") pathParam,
String entity) {
// beanParam.getPathParam() == pathParam

}

关于如何接受json参数及回复json数据,将会在后续Jersey对JSON的支持一节中介绍。这个可能是很多人比较关心的一个知识点。敬请期待...。

3.子资源

在类上添加@Path注解后,我们称该类为根资源类。如果在根资源类中的方法添加该注解则称该方法为子资源。

@Singleton
@Path("/printers")
public class PrintersResource {

@GET
@Produces({"application/json", "application/xml"})
public WebResourceList getMyResources() { ... }

@GET @Path("/list")
@Produces({"application/json", "application/xml"})
public WebResourceList getListOfPrinters() { ... }

@GET @Path("/jMakiTable")
@Produces("application/json")
public PrinterTableModel getTable() { ... }

@GET @Path("/jMakiTree")
@Produces("application/json")
public TreeModel getTree() { ... }

@GET @Path("/ids/{printerid}")
@Produces({"application/json", "application/xml"})
public Printer getPrinter(@PathParam("printerid") String printerId) { ... }

@PUT @Path("/ids/{printerid}")
@Consumes({"application/json", "application/xml"})
public void putPrinter(@PathParam("printerid") String printerId, Printer printer) { ... }

@DELETE @Path("/ids/{printerid}")
public void deletePrinter(@PathParam("printerid") String printerId) { ... }
}

如果添加@Path的方法没有使用类似@GET、@POST之类的注解时,则称该方法为子资源定位器,例如下面的getItemContentResource()方法。

@Path("/item")
public class ItemResource {
@Context UriInfo uriInfo;

@Path("content")
public ItemContentResource getItemContentResource() {
return new ItemContentResource();
}

@GET
@Produces("application/xml")
public Item get() { ... }
}
}

public class ItemContentResource {

@GET
public Response get() { ... }

@PUT
@Path("{version}")
public void put(@PathParam("version") int version,
@Context HttpHeaders headers,
byte[] in) {
...
}
}

子资源类ItemContentResource中的两个方法如何访问呢?

对于get(),访问路径为:/item/content | Get请求

对于put(), 访问路径为:/item/content/{version} | put请求

子资源类通过new的方式其生命周期不会被Jersey运行时环境所管理,如果要纳入Jersey运行时管理中,则需要按照下面的程序来创建对象:

import javax.inject.Singleton;

@Path("/item")
public class ItemResource {
@Path("content")
public Class<ItemContentSingletonResource> getItemContentResource() {
return ItemContentSingletonResource.class;
}
}

@Singleton
public class ItemContentSingletonResource {
// this class is managed in the singleton life cycle
}

同样也可以使用如下代码生成被Jersey运行时环境所管理的子资源类实例:

import org.glassfish.jersey.server.model.Resource;

@Path("/item")
public class ItemResource {

@Path("content")
public Resource getItemContentResource() {
return Resource.from(ItemContentSingletonResource.class);
}
}

@Singleton
public class ItemContentSingletonResource {
// this class is managed in the singleton life cycle
}

4.根资源的生命周期

默认情况下,根资源在每次请求到来时都会产生新的资源对象,其对应的注解为@RequestScoped 或不加。

@PerLookup :每个处理都会产生新的对象尽管是在同一个请求中。

@Singleton : 只会产生一个根资源对象,即单例。

5.参数注入规则

一般情况下,参数可以注入到属性,构造方法参数,根资源/子资源的方法参数,set方法【限制在@Context,领会不深,尽量少用】。

@Path("{id:\\d+}")
public class InjectedResource {
// Injection onto field
@DefaultValue("q") @QueryParam("p")
private String p;

// Injection onto constructor parameter
public InjectedResource(@PathParam("id") int id) { ... }

// Injection onto resource method parameter
@GET
public String get(@Context UriInfo ui) { ... }

// Injection onto sub-resource resource method parameter
@Path("sub-id")
@GET
public String get(@PathParam("sub-id") String id) { ... }

// Injection onto sub-resource locator method parameter
@Path("sub-id")
public SubResource getSubResource(@PathParam("sub-id") String id) { ... }

// Injection using bean setter method
@HeaderParam("X-header")
public void setHeader(String header) { ... }
}

针对@Singleton注解的根资源,注入时有一些限制条件。

@ 请求的参数不能注入到根资源类的实例变量及构造器中-单例原因,因此如下代码是不允许的

Path("resource")
@Singleton
public static class MySingletonResource {

@QueryParam("query")
String param; // WRONG: initialization of application will fail as you cannot
// inject request specific parameters into a singleton resource.

@GET
public String get() {
return "query param: " + param;
}
}

使用@Singleton时,上面的 规则不适用于
HttpHeaders
,
Request
,
UriInfo
,
SecurityContext


@Path("resource")
@Singleton
public static class MySingletonResource {
@Context
Request request; // this is ok: the proxy of Request will be injected into this singleton

public MySingletonResource(@Context SecurityContext securityContext) {
// this is ok too: the proxy of SecurityContext will be injected
}

@GET
public String get() {
return "query param: " + param;
}
}

以下代码示例了所有的注入可能性:

@Path("resource")
public static class SummaryOfInjectionsResource {
@QueryParam("query")
String param; // injection into a class field

@GET
public String get(@QueryParam("query") String methodQueryParam) {
// injection into a resource method parameter
return "query param: " + param;
}

@Path("sub-resource-locator")
public Class<SubResource> subResourceLocator(@QueryParam("query") String subResourceQueryParam) {
// injection into a sub resource locator parameter
return SubResource.class;
}

public SummaryOfInjectionsResource(@QueryParam("query") String constructorQueryParam) {
// injection into a constructor parameter
}

@Context
public void setRequest(Request request) {
// injection into a setter method
System.out.println(request != null);
}
}

public static class SubResource {
@GET
public String get() {
return "sub resource";
}
}

@FormParam只能用于根资源及子资源的方法上

6. @Context的使用

除了上面展示的可以使用@Context注入外,当使用servlet容器部署时,ServletConfig, ServletContext, HttpServletRequestHttpServletResponse 都是可以注入到资源类的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Jersey JAX-RS