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>

    该值将是与指定模板参数匹配的路径片段对应的 PathSegment 对象列表。

  • 原语,如 intcharlong
  • 具有接受单个 String 参数的构造器的对象
  • 具有静态 valueOf () 方法的对象,它接受单个 String 参数

使用查询参数

在 Web 上传递信息的常见方法是使用 URI 中的 查询参数。查询参数会出现在 URI 的末尾,并由问号(?)分隔至 URI 的资源位置部分。它们由一个或多个名称值对组成,其中 name 和 值由等号(=)分隔。当指定多个查询参数时,对通过分号(;)或 符号( 和 )相互分离例 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)
  {
    ...
  }
  ...
}

要处理 HTTP POST/monstersforhire/daikaiju?id=jonas the updateMonster () 方法 的类型 被设置为 daikaiju,并将 id 设置为 jonas

使用列表参数

URI 列表参数(如 URI 查询参数)是 name/value 对,可以提供选择资源的额外信息。与查询参数不同,列表参数可在 URI 中的任何位置显示,它们使用分号(;)从 URI 的分层路径段中分离。/mostersforhire/daikaiju;id=jonas 有一个名为 id/monstersforhi/japan; type =daikaiju/flying;wingspan=40 的两个列表,称为 name 和 wingspan

注意

当计算资源 URI 时,不会评估列表参数。因此,用来处理请求 URI /monstersforhire/japan;type=daikaiju/flying;wingspan=40 是 /monstersforhire/japan/flying 的资源的 URI /monstersforre/japan/flying

matrix 参数的值注入使用 javax.ws.rs.MatrixParam 注解的字段、参数或 bean 属性。该注释采用单个参数,用于标识提取值并注入到指定字段、bean 属性或参数中的 list 参数的名称。@MatrixParam 注释支持 “支持的数据类型”一节 中描述的类型。

例 47.4 “使用列表参数中的数据的资源方法” 显示一个资源方法,用于将列表参数 类型和 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)
  {
    ...
  }
  ...
}

要处理 HTTP POST/monstersforhire;type=daikaiju;id=whale the updateMonster () 方法的 类型设置为 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 decoding

默认情况下,所有请求 URI 都会被解码。因此 URI /monster/night%20stalker 和 URI /monster/night stalker 等效。自动 URI 解码使得将 ASCII 字符设置之外的字符作为参数轻松发送。

如果您不想自动解码 URI,您可以使用 javax.ws.rs.Encoded 注解来取消激活 URI decoding。该注解可用于在以下级别取消激活 URI decoding:

  • 使用 @Encoded 注释对类进行类进行分类,可停用课堂上所有参数、字段和 bean 属性的 URI 解码。
  • 方法级别的使用 @Encoded 注释的方法取消激活了类的所有参数的 URI 解码。
  • 参数/字段使用 @Encoded 注释定义参数或字段将停用 URI 解码类的所有参数。

例 47.5 “禁用 URI decoding” 显示 getMonster () 方法不使用 URI decoding 的资源。addMonster () 方法仅禁用 type 参数的 URI decoding。

例 47.5. 禁用 URI decoding

@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 标头注入类 最旧的 字段的代码。

注入 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” 显示将 句柄 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 注解从表单 data 中提取字段值,并将值注入到资源方法参数中。该注释采用单一参数,用于指定它提取值的字段键。关联的参数必须符合 “支持的数据类型”一节 中描述的数据类型。

重要

JAX-RS API Javadoc指出 @FormParam 注释可以放在字段、方法和参数上。但是,@FormParam 注释仅在放置资源方法参数时有意义。

示例

将表单数据注入资源方法参数 显示将表单数据注入参数的资源方法。该方法假定客户端的格式包含三个字段:标题标签body- 包含字符串数据。

将表单数据注入资源方法参数

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, ... )
  ...

该注解必须在参数、bean 或 field 之前出现,它将生效。相对于附带的注入注解的 @DefaultValue 注释的位置无关紧要。

@DefaultValue 注释采用单参数。如果无法根据注入注解提取正确的数据,则此参数是注入到字段的值。该值可以是任何 String 值。该值应该与关联字段的类型兼容。例如,如果关联字段类型为 int,则默认值 blue 除外。

处理列表和集合

如果注解的参数的类型,bean 或 field 为 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 () 方法。该方法需要两个查询参数,即 idtype,并附加到 URI。因此,使用 URI baseURI/monster?id=1&type=fomóiriGET 请求会用其中一个 ID 返回 Fomóiri。

由于 @DefaultValue 注释放在这两个参数上,因此如果省略了查询参数,getMonster () 方法可以发挥作用。发送到 baseURI/monster GET 请求等同于使用 URI baseURI/monster?id=42&type=bogeyman 的 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

字段

per-request (默认)

METHOD (字母属性)

per-request (默认)

without 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 类 表表 的字段。实际上,您可以使用 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 中,您可以使用单个 @BeanParam 注释替换资源方法签名中的所有 @FormParam 注解,如下所示。现在,当表单被发布到可 订购 的资源方法时,JAX-RS 运行时会自动创建 TableOrder 实例、orderBean 并在 bean 类上注入参数注解指定的所有数据。