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 ajavax.ws.rs.ext.ParamConverter
instance capable of a "from string" conversion for the type. or 【这一点不是很明白,先mark吧】 -
List<T>
,Set<T>
orSortedSet<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, HttpServletRequest ,HttpServletResponse 都是可以注入到资源类的。