47.2. 使用 JAX-RS API
47.2.1. JAX-RS 注解类型
标准 JAX-RS API 指定可用于将值注入字段、bean 属性和方法参数的注释。注解可以被分成三种不同的类型:
47.2.2. 从请求 URI 注入数据
概述
设计 RESTful Web 服务的最佳实践之一就是每个资源都应该有一个唯一的 URI。开发人员可以使用这个原则来为底层资源实施提供大量信息。在为资源设计 URI 模板时,开发人员可以构建模板,使其包含可注入到资源实施中的参数信息。开发人员还可以利用查询和列表参数将信息发送到资源实施中。
从 URI 的路径获取数据
获取资源信息的更常见机制之一是通过为资源创建 URI 模板时使用的变量。这可以通过 javax.ws.rs.PathParam 注释来完成。@PathParam 注释具有一个参数,用于标识要从中注入数据的 URI 模板变量。
在 例 47.1 “从 URI 模板变量注入数据” 中,@PathParam 注释指定 URI 模板变量 颜色 的值被注入到 itemColor 字段中。
例 47.1. 从 URI 模板变量注入数据
import javax.ws.rs.Path;
import javax.ws.rs.PathParam
...
@Path("/boxes/{shape}/{color}")
class Box
{
...
@PathParam("color")
String itemColor;
...
}
@PathParam 注释支持的数据类型与 “支持的数据类型”一节 中描述的数据类型不同。@PathParam 注释注入数据的实体必须是以下类型之一:
PathSegment
该值将是路径匹配部分的最终片段。
List<PathSegment>
该值将是与 named 模板参数匹配的 path segment 对应的 PathSegment 对象列表。
-
原语,如
int、char或long -
具有接受单个
String参数的构造器的对象 -
具有静态
valueOf ()方法的对象,它接受单个String参数
使用查询参数
在 Web 上传递信息的一个常见方法是使用 URI 中的查询参数。查询参数出现在 URI 的末尾,并用问号(?)与 URI 的资源位置部分分隔开。它们由一个或者多个名称值对组成,其中名称和值用等号(=)分隔。当指定多个查询参数时,对通过分号(;)或 符号(和)相互分隔。例 47.2 “带有查询字符串的 URI” 显示带有查询参数的 URI 语法。
例 47.2. 带有查询字符串的 URI
http://fusesource.org?name=value;name2=value2;...
您可以使用分号或 符号来分隔查询参数,但不能同时使用两者。
javax.ws.rs.QueryParam 注释提取查询参数的值并将其注入 JAX-RS 资源。该注解使用一个参数来标识从中提取值的查询参数的名称,并注入到指定字段、bean 属性或参数中。@QueryParam 注释支持 “支持的数据类型”一节 中描述的类型。
例 47.3 “使用查询参数中的数据的资源方法” 显示资源方法,将查询参数 id 的值注入方法的 id 参数。
例 47.3. 使用查询参数中的数据的资源方法
import javax.ws.rs.QueryParam;
import javax.ws.rs.PathParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
...
@Path("/monstersforhire/")
public class MonsterService
{
...
@POST
@Path("/{type}")
public void updateMonster(@PathParam("type") String type,
@QueryParam("id") String id)
{
...
}
...
}
要处理到 /monstersforhire/daikaiju?id=jonas 的 HTTP POST,将 updateMonster () 方法的类型设为 daikaiju,ID 设置为 jonas。
使用列表参数
URI 列表参数(如 URI 查询参数)是名称/值对,可以提供额外的信息选择资源。与查询参数不同,列表参数可以在 URI 的任意位置显示,它们使用分号(;)与 URI 的层次结构路径段分隔开。/mostersforhire/daikaiju;id=jonas 有一个 matrix 参数,名为 id 和 /monstersforhire/japan;type=daikaiju/flying;wingspan=40 有两个列表参数,称为 type 和 wingspan。
计算资源的 URI 时,不会评估列表参数。因此,用于查找用于处理请求 URI /monstersforhire/japan;type=daikaiju/flying;wingspan=40 是 /monstersforhire/japan/flying 的 URI。
matrix 参数的值使用 javax.ws.rs.MatrixParam 注释注入到字段、参数或 bean 属性中。该注解使用一个参数来标识从中提取值的 matrix 参数的名称,并注入到指定字段、bean 属性或参数中。@MatrixParam 注释支持 “支持的数据类型”一节 中描述的类型。
例 47.4 “使用列表参数中的数据的资源方法” 显示一个资源方法,将 matrix 参数 type 和 id 的值注入方法的参数。
例 47.4. 使用列表参数中的数据的资源方法
import javax.ws.rs.MatrixParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
...
@Path("/monstersforhire/")
public class MonsterService
{
...
@POST
public void updateMonster(@MatrixParam("type") String type,
@MatrixParam("id") String id)
{
...
}
...
}
要处理到 /monstersforhire 的 HTTP POST,type=daikaiju;id=whale updateMonster () 方法的 type 设置为 daikaiju,ID 设置为 whale。
JAX-RS 一次评估 URI 中的所有列表参数,因此无法对 URI 中的列表参数位置强制限制。例如: /monstersforhire/japan;type=daikaiju/flying;wingspan=40 , /monstersforhire/japan/flying;type=daikaiju;wingspan=40, 和 /monstersforhire/japan; type=daikaiju;wingspan=40/flying 都被视为相当于使用 JAX-RS API 实施的 RESTful Web 服务。
禁用 URI 解码
默认情况下,所有请求 URI 都解码。因此,URI /monster/night%20stalker 和 URI /monster/night stalker 等效。自动 URI 解码使得在 ASCII 字符集外轻松发送字符作为参数。
如果您不想自动解码 URI,您可以使用 javax.ws.rs.Encoded 注解来停用 URI 解码。注解可用于在以下级别取消激活 URI 解码:
-
class level-Decorating a class with the
@Encoded注释会取消激活类中所有参数、字段和 bean 属性的 URI 解码。 -
方法 level-Decorating 方法带有
@Encoded注释可停用类所有参数的 URI 解码。 -
参数/字段级别取消参数或字段(带有
@Encoded注释)取消激活类所有参数的 URI 解码。
例 47.5 “禁用 URI 解码” 显示其 getMonster () 方法不使用 URI 解码的资源。addMonster () 方法只禁用 type 参数的 URI 解码。
例 47.5. 禁用 URI 解码
@Path("/monstersforhire/")
public class MonsterService
{
...
@GET
@Encoded
@Path("/{type}")
public Monster getMonster(@PathParam("type") String type,
@QueryParam("id") String id)
{
...
}
@PUT
@Path("/{id}")
public void addMonster(@Encoded @PathParam("type") String type,
@QueryParam("id") String id)
{
...
}
...
}错误处理
如果尝试使用其中一个 URI 注入注解注入数据时出现错误,则会生成 WebApplicationException 异常嵌套原始异常。WebApplicationException 异常的状态设置为 404。
47.2.3. 从 HTTP 消息标头注入数据
概述
在正常使用请求消息中的 HTTP 标头传递有关消息的通用信息、在传输中如何处理它,以及预期响应的详细信息。虽然通常识别和使用几个标准标头,但 HTTP 规格允许任何名称/值对用作 HTTP 标头。JAX-RS API 提供了将 HTTP 标头信息注入资源实施的简单机制。
最常用的 HTTP 标头之一是 cookie。Cookie 允许 HTTP 客户端和服务器在多个请求/响应序列之间共享静态信息。JAX-RS API 提供了注释,可直接将数据从 Cookie 注入资源实施。
从 HTTP 标头注入信息
javax.ws.rs.HeaderParam 注释用于将 HTTP 标头字段中的数据注入参数、字段或 bean 属性。它有一个单一参数,用于指定从中提取值并注入到资源实施中的 HTTP 标头字段的名称。关联的参数、字段或 bean 属性必须符合 “支持的数据类型”一节 中描述的数据类型。
注入 If-Modified-Since 标头 显示将 HTTP If-Modified-Since 标头的值注入类 oldestDate 字段的代码。
注入 If-Modified-Since 标头
import javax.ws.rs.HeaderParam; ... class RecordKeeper { ... @HeaderParam("If-Modified-Since") String oldestDate; ... }
从 Cookie 注入信息
Cookie 是特殊的 HTTP 标头类型。它们由一个或多个名称/值对组成,这些对传递到第一个请求上的资源实施。在第一个请求后,cookie 会在供应商和消费者之间返回,并显示每个消息。只有消费者,因为它们生成请求才能更改 Cookie。Cookie 通常用于跨多个请求/响应序列、存储用户设置和其他可保留数据维护会话。
javax.ws.rs.CookieParam 注释从 Cookie 的字段中提取值,并将它注入到资源实施中。它使用一个参数,用于指定要提取值的 Cookie 字段的名称。除了 “支持的数据类型”一节 中列出的数据类型外,@CookieParam 的实体也是 Cookie 对象。
例 47.6 “注入 Cookie” 显示将 handle cookie 的值注入 CB 类中的一个字段的代码。
例 47.6. 注入 Cookie
import javax.ws.rs.CookieParam; ... class CB { ... @CookieParam("handle") String handle; ... }
错误处理
如果尝试使用其中一个 HTTP 消息注入数据时出现错误,则会生成 WebApplicationException 异常嵌套原始异常。WebApplicationException 异常的状态设置为 400。
47.2.4. 从 HTML 表单注入数据
概述
HTML 表单是一种从用户获取信息的简单方法,而且易于创建。表单数据可用于 HTTP GET 请求和 HTTP POST 请求:
- GET
-
当作为 HTTP
GET请求的一部分发送表单数据时,数据将作为一组查询参数附加到 URI 中。“使用查询参数”一节 中讨论从查询参数注入数据。 - POST
-
当将数据作为 HTTP
POST请求的一部分发送时,数据将放置在 HTTP 消息正文中。可以使用支持表单数据的常规实体参数来处理表单数据。也可以通过使用@FormParam注释来提取数据并注入资源方法参数来处理它。
使用 @FormParam 注释来注入表单数据
javax.ws.rs.FormParam 注释从表单数据中提取字段值,并将值注入到资源方法参数中。该注解使用一个参数,用于指定从中提取值的字段的键。关联的参数必须符合 “支持的数据类型”一节 中描述的数据类型。
JAX-RS API Javadoc 表示 @FormParam 注释可以放在字段、方法和参数上。但是,只有在放在资源方法参数上时,@FormParam 注释才有意义。
示例
将表单数据注入资源方法参数 显示将数据注入其参数的资源方法。该方法假定客户端的表单包含三个字段,标题为、标签,以及包含字符串数据 的正文。
将表单数据注入资源方法参数
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
...
@POST
public boolean updatePost(@FormParam("title") String title,
@FormParam("tags") String tags,
@FormParam("body") String post)
{
...
}
47.2.5. 指定要注入的默认值
概述
要为更强大的服务实现提供,您可能需要确保可以将任何可选参数设置为默认值。这对从查询参数和列表参数获取的值特别有用,因为输入较长的 URI 字符串非常容易出错。您可能还想为从 Cookie 中提取的参数设置默认值,因为请求系统没有适当的信息来构造带有所有值的 Cookie。
javax.ws.rs.DefaultValue 注释可与以下注入注解结合使用:
-
@PathParam -
@QueryParam -
@MatrixParam -
@FormParam -
@HeaderParam -
@CookieParam
@DefaultValue 注释指定在请求中没有与注入注释对应的数据时使用的默认值。
语法
设置参数默认值的语法 显示使用 @DefaultValue 注释的语法。
设置参数默认值的语法
import javax.ws.rs.DefaultValue; ... void resourceMethod(@MatrixParam("matrix") @DefaultValue("value) int someValue, ... ) ...
该注解必须在 parameter、bean 或 field 之前,它将生效。与附带注入注释相关的 @DefaultValue 注释的位置无关紧要。
@DefaultValue 注释采用单个参数。如果无法根据注入注解提取正确的数据,则此参数值是将注入字段的值。该值可以是任意 String 值。该值应与相关字段的类型兼容。例如,如果关联的字段是 int 类型,则默认值 blue 会产生异常。
处理列表和集合
如果注解的参数的类型,bean 或字段是 List、Set 或 SortedSet,则生成的集合将从提供的默认值映射的一个条目。
示例
设置默认值 演示了使用 @DefaultValue 为注入值的字段指定默认值的两个示例。
设置默认值
import javax.ws.rs.DefaultValue;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@Path("/monster")
public class MonsterService
{
@Get
public Monster getMonster(@QueryParam("id") @DefaultValue("42") int id,
@QueryParam("type") @DefaultValue("bogeyman") String type)
{
...
}
...
}
当 GET 请求发送到 baseURI/monster 时,会调用 设置默认值 中的 getMonster () 方法。该方法需要两个查询参数: id 和 type,并附加到 URI 中。因此,使用 URI baseURI/monster?id=1&type=fomurllibiri 的 GET 请求会返回 FomedServiceSetiri,ID 为 one。
由于 @DefaultValue 注释放在这两个参数上,因此如果忽略了查询参数,getMonster () 方法可以运行。发送到 baseURI/monster 的 请求等同于使用 URI baseURI/monster?id=42&type=bogeyman 的 GET 请求。
GET
47.2.6. 将参数注入 Java Bean
概述
当通过 REST 发布 HTML 表单时,服务器端的常见模式是创建一个 Java bean 来封装形式收到的所有数据(以及可能来自其他参数和 HTML 标头的数据)。通常,创建此 Java bean 分为两个步骤:资源方法通过注入接收表单值(例如,通过将 @FormParam 注解添加到其方法参数),而资源方法随后调用 bean 的构造器,以传递表单数据。
使用 JAX-RS 2.0 @BeanParam 注释,可以在单一步骤中实施此模式。表单数据可以直接注入到 bean 类的字段,而 bean 本身则由 JAX-RS 运行时自动创建。例如,这最容易解释。
注入目标
@BeanParam 注释可以附加到资源方法参数、资源字段或 bean 属性。但是,参数目标是可用于所有资源类生命周期的唯一目标类型。其他类型的目标仅限于每个请求的生命周期。表 47.1 “@BeanParam Injection Targets” 中总结了这种情况。
表 47.1. @BeanParam Injection Targets
| 目标 | 资源类生命周期 |
|---|---|
|
| All |
|
| 按请求(默认) |
|
| 按请求(默认) |
没有 BeanParam 注解的示例
以下示例演示了如何使用传统方法捕获 Java bean 中的表单数据(不使用 @BeanParam):
// Java
import javax.ws.rs.POST;
import javax.ws.rs.FormParam;
import javax.ws.rs.core.Response;
...
@POST
public Response orderTable(@FormParam("orderId") String orderId,
@FormParam("color") String color,
@FormParam("quantity") String quantity,
@FormParam("price") String price)
{
...
TableOrder bean = new TableOrder(orderId, color, quantity, price);
...
return Response.ok().build();
}
在本例中,orderTable 方法处理了一个表单,用于为周年网站订购表的数量。发布订购表单后,表单值将注入到 orderTable 方法的参数中,而 orderTable 方法会明确创建 TableOrder 类的实例,使用注入的形式数据。
带有 BeanParam 注解的示例
上一示例可以重构,以利用 @BeanParam 注释。使用 @BeanParam 方法时,表单参数可以直接注入到 bean 类的字段 TableOrder。实际上,您可以使用 bean 类中的任何标准 JAX-RS 参数注释:包括 @PathParam,@QueryParam,@FormParam,@MatrixParam,@CookieParam, 和 @HeaderParam。处理表单的代码可以被重构,如下所示:
// Java
import javax.ws.rs.POST;
import javax.ws.rs.FormParam;
import javax.ws.rs.core.Response;
...
public class TableOrder {
@FormParam("orderId")
private String orderId;
@FormParam("color")
private String color;
@FormParam("quantity")
private String quantity;
@FormParam("price")
private String price;
// Define public getter/setter methods
// (Not shown)
...
}
...
@POST
public Response orderTable(@BeanParam TableOrder orderBean)
{
...
// Do whatever you like with the 'orderBean' bean
...
return Response.ok().build();
}
现在,表单注解已添加到 bean 类 TableOrder 中,您可以将资源方法签名中的所有 @FormParam 注解替换为单个 @BeanParam 注释,如下所示。现在,当表单发布到 orderTable 资源方法时,JAX-RS 运行时会自动创建一个 TableOrder 实例,orderBean,并在 bean 类上注入参数注解指定的所有数据。