4.2. 使用 REST DSL 定义服务

REST DSL 是一个 facade

REST DSL 实际上是一个 教职,为在 Java DSL 或 XML DSL (域特定语言)中定义 REST 服务提供了简化的语法。REST DSL 实际上不提供 REST 实施,它只是一个 与现有 REST 实施相关的打包程序(在 Apache Camel 中有多个)。

REST DSL 的优点

REST DSL 打包程序层提供以下优点:

  • 定义 REST 服务的现代易用语法。
  • 与多个不同的 Apache Camel 组件兼容。
  • OpenAPI 集成(通过 camel-openapi-java 组件)。

与 REST DSL 集成的组件

因为 REST DSL 不是实际的 REST 实施,您需要的第一个操作之一是选择 Camel 组件来提供底层实施。以下 Camel 组件目前与 REST DSL 集成:

注意

Rest 组件( camel-core)不是 REST 实施。与 REST DSL 一样,Rest 组件是教职,提供了使用 URI 语法定义 REST 服务的简单语法。Rest 组件还需要底层的 REST 实施。

配置 REST DSL 以使用 REST 实施

要指定 REST 实施,您可以使用 restConfiguration () 构建器(Java DSL 中)或 restConfiguration 元素(在 XML DSL 中)。例如,要将 REST DSL 配置为使用 Spark-Rest 组件,您可以使用类似于 Java DSL 中的以下构建程序表达式:

restConfiguration().component("spark-rest").port(9091);

您要在 XML DSL 中使用类似于以下内容的元素(作为 camelContext的子项):

<restConfiguration component="spark-rest" port="9091"/>

语法

定义 REST 服务的 Java DSL 语法如下:

rest("BasePath").Option().
    .Verb("Path").Option().[to() | route().CamelRoute.endRest()]
    .Verb("Path").Option().[to() | route().CamelRoute.endRest()]
    ...
    .Verb("Path").Option().[to() | route().CamelRoute];

其中 CamelRoute 是一个可选的嵌入式 Camel 路由(使用标准 Java DSL 语法进行路由定义)。

REST 服务定义以 rest () 关键字开头,后跟一个或多个处理特定 URL 路径片段的动词。HTTP 动词可以是 get ()、head ()、put ()、post ()、 delete ()、 patch () verb () 之一。每个动词 子句都可以使用以下语法之一:

  • to () 关键字结尾的 动词子。例如:

    get("...").Option()+.to("...")
  • route () 关键字结尾的动词子(用于嵌入 Camel 路由)。例如:

    get("...").Option()+.route("...").CamelRoute.endRest()

使用 Java 的 REST DSL

在 Java 中,使用 REST DSL 定义服务,将 REST 定义放入 RouteBuilder.configure () 方法的正文中,就像常规的 Apache Camel 路由一样。例如,要使用 Spark-Rest 组件的 REST DSL 定义一个简单的 Hello World 服务,请定义以下 Java 代码:

restConfiguration().component("spark-rest").port(9091);

rest("/say")
    .get("/hello").to("direct:hello")
    .get("/bye").to("direct:bye");

from("direct:hello")
    .transform().constant("Hello World");
from("direct:bye")
    .transform().constant("Bye World");

前面的示例具有三种不同的构建器类型:

restConfiguration()
配置 REST DSL,以使用特定的 REST 实施(Spark-Rest)。
rest()
使用 REST DSL 定义服务。每个动词子句都由 to () 关键字终止,它将传入的消息转发到 直接 端点(同一应用中的 直接 组件测试路由)。
from()
定义常规 Camel 路由。

使用 XML 的 REST DSL

在 XML 中,要使用 XML DSL 定义服务,请将 rest 元素定义为 camelContext 元素的子级。例如,要使用 Spark-Rest 组件使用 REST DSL 定义一个简单的 Hello World 服务,请定义以下 XML 代码(在 Blueprint 中):

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  <restConfiguration component="spark-rest" port="9091"/>

  <rest path="/say">
    <get uri="/hello">
      <to uri="direct:hello"/>
    </get>
    <get uri="/bye">
      <to uri="direct:bye"/>
    </get>
  </rest>

  <route>
    <from uri="direct:hello"/>
    <transform>
      <constant>Hello World</constant>
    </transform>
  </route>
  <route>
    <from uri="direct:bye"/>
    <transform>
      <constant>Bye World</constant>
    </transform>
  </route>
</camelContext>

指定基本路径

rest () 关键字(Java DSL)或 rest 元素的 path 属性(XML DSL)允许您定义基本路径,然后在所有 verb 子句中的路径加上前缀。例如,给定以下 Java DSL 片段:

rest("/say")
    .get("/hello").to("direct:hello")
    .get("/bye").to("direct:bye");

或者给定以下 XML DSL 片段:

<rest path="/say">
  <get uri="/hello">
    <to uri="direct:hello"/>
  </get>
  <get uri="/bye" consumes="application/json">
    <to uri="direct:bye"/>
  </get>
</rest>

REST DSL 构建器提供以下 URL 映射:

/say/hello
/say/bye

基本路径是可选的。如果您愿意,可以在每个 verb 子句中指定完整路径:

rest()
    .get("/say/hello").to("direct:hello")
    .get("/say/bye").to("direct:bye");

使用动态到

REST DSL 支持 toD dynamic to 参数。使用此参数指定 URI。

例如,在 JMS 中,可以通过以下方式定义动态端点 URI:

public void configure() throws Exception {
   rest("/say")
     .get("/hello/{language}").toD("jms:queue:hello-${header.language}");
}

在 XML DSL 中,相同的详情类似如下:

<rest uri="/say">
  <get uri="/hello//{language}">
    <toD uri="jms:queue:hello-${header.language}"/>
  </get>
<rest>

有关 toD 动态到参数的更多信息,请参阅 “动态到”一节

URI 模板

在动词参数中,您可以指定一个 URI 模板,该模板允许您在指定属性中捕获特定的路径片段(然后将其映射到 Camel 消息标头)。例如,如果您希望对 Hello World 应用进行个性化,使其按名称问候到调用器,您可以定义一个类似以下内容的 REST 服务:

rest("/say")
    .get("/hello/{name}").to("direct:hello")
    .get("/bye/{name}").to("direct:bye");

from("direct:hello")
    .transform().simple("Hello ${header.name}");
from("direct:bye")
    .transform().simple("Bye ${header.name}");

URI 模板捕获 {name} 路径分段的文本,并将这个捕获的文本复制到 名称 消息标头中。如果您通过发送以 /say/hello/Joe 结尾的 URL 发送 GET HTTP Request,则 HTTP 响应为 Hello Joe

嵌入式路由语法

您可以使用 to () 关键字(Java DSL)或 to element (XML DSL)终止动词子,而是使用 route () 关键字(Java DSL)或路由元素(XML DSL)将 Apache Camel 路由 直接嵌入到 REST DSL 中。route () 关键字允许您将路由嵌入到 verb 子句中,语法如下:

RESTVerbClause.route("...").CamelRoute.endRest()

其中 endRest () 关键字(仅限 Java DSL)是一个必要的标点符号,它可让您分隔动词子(当 rest () 构建器中有多个动词句)。

例如,您可以重构 Hello World 示例以使用嵌入式 Camel 路由,如 Java DSL 所示:

rest("/say")
    .get("/hello").route().transform().constant("Hello World").endRest()
    .get("/bye").route().transform().constant("Bye World");

如下为 XML DSL 中:

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  ...
  <rest path="/say">
    <get uri="/hello">
      <route>
        <transform>
          <constant>Hello World</constant>
        </transform>
      </route>
    </get>
    <get uri="/bye">
      <route>
        <transform>
          <constant>Bye World</constant>
        </transform>
      </route>
    </get>
  </rest>
</camelContext>
注意

如果您在当前 CamelContext 中定义任何例外条款(使用 onException ()或 interceptors)或拦截器(使用拦截器),则这些异常子和拦截器在嵌入的路由中也处于活动状态。

REST DSL 和 HTTP 传输组件

如果您没有显式配置 HTTP 传输组件,则 REST DSL 会自动通过检查 classpath 上的可用组件来发现要使用的 HTTP 组件。REST DSL 查找任何 HTTP 组件的默认名称,并使用它找到的第一个名称。如果 classpath 中没有 HTTP 组件,而您没有显式配置 HTTP 传输,则默认的 HTTP 组件是 camel-http

指定请求和响应的内容类型

您可以使用 consumes () 过滤 HTTP 请求和响应 的内容类型,并在 Java 中生成s () 选项,或者在 XML 中消耗和 生成 属性。例如,一些常见的内容类型(称为 互联网媒体类型)是以下内容:

  • text/plain
  • text/html
  • text/xml
  • application/json
  • application/xml

内容类型在 REST DSL 中的 verb 子句上作为选项指定。例如,若要将动词 子句限制为只接受 text/plain HTTP 请求,要仅发送 text/html HTTP 响应,您需要使用类似如下的 Java 代码:

rest("/email")
    .post("/to/{recipient}").consumes("text/plain").produces("text/html").to("direct:foo");

在 XML 中,您可以设置 消耗 并生成 属性,如下所示:

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  ...
  <rest path="/email">
    <post uri="/to/{recipient}" consumes="text/plain" produces="text/html">
      <to "direct:foo"/>
    </get>
  </rest>
</camelContext>

您还可以指定使用 () 的参数,或以逗号分隔列表的形式 生成s ()。例如,使用 ("text/plain, application/json")

其他 HTTP 方法

一些 HTTP 服务器实施支持额外的 HTTP 方法,它们不是由 REST DSL 中的标准动词集合提供,get ()、 head ()、put () post ()、 delete ()、 patch ()。要访问其他 HTTP 方法,您可以在 XML DSL 中使用 generic 关键字 verb () 和通用元素 动词

例如,在 Java 中实施 TRACE HTTP 方法:

rest("/say")
    .verb("TRACE", "/hello").route().transform();

其中 transform ()IN 消息的正文复制到 OUT 消息的正文,从而回显 HTTP 请求。

在 XML 中实施 TRACE HTTP 方法:

<camelContext xmlns="http://camel.apache.org/schema/blueprint">
  ...
  <rest path="/say">
    <verb uri="/hello" method="TRACE">
      <route>
        <transform/>
      </route>
    </get>
</camelContext>

定义自定义 HTTP 错误消息

如果您的 REST 服务需要发送错误消息作为其响应,您可以定义自定义 HTTP 错误消息,如下所示:

  1. 通过将 Exchange.HTTP_RESPONSE_CODE 标头密钥设置为错误代码值(如 400404 等等),指定 HTTP 错误代码。此设置指示您要发送错误消息回复的 REST DSL,而不是常规响应。
  2. 将消息正文填充您的自定义错误消息。
  3. 如果需要,设置 Content-Type 标头。
  4. 如果您的 REST 服务被配置为 marshal to and from Java 对象(即 bindingMode is enabled),则您应该确保启用 skipBindingOnErrorCode 选项(默认为 )。这是为了确保 REST DSL 在发送响应时不会尝试传播消息正文。

    有关对象绑定的详情,请参阅 第 4.3 节 “从 Java 对象提取至和进行 marshalling”

以下 Java 示例演示了如何定义自定义错误消息:

// Java
// Configure the REST DSL, with JSON binding mode
restConfiguration().component("restlet").host("localhost").port(portNum).bindingMode(RestBindingMode.json);

// Define the service with REST DSL
rest("/users/")
    .post("lives").type(UserPojo.class).outType(CountryPojo.class)
        .route()
            .choice()
                .when().simple("${body.id} < 100")
                    .bean(new UserErrorService(), "idTooLowError")
                .otherwise()
                    .bean(new UserService(), "livesWhere");

在本例中,如果输入 ID 小于 100,我们会返回一个自定义错误消息,使用 UserErrorService bean,它实施如下:

// Java
public class UserErrorService {
    public void idTooLowError(Exchange exchange) {
        exchange.getIn().setBody("id value is too low");
        exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "text/plain");
        exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
    }
}

UserErrorService bean 中,我们定义了自定义错误消息,并将 HTTP 错误代码设置为 400

参数默认值

可以为传入的 Camel 消息的标头指定默认值。

您可以使用查询参数中的关键字(如 详细 )指定默认值。例如,在以下代码中,默认值为 false。这意味着,如果为包含 verbose 键的标头提供任何其他值,则 false 将作为默认值进行插入。

rest("/customers/")
    .get("/{id}").to("direct:customerDetail")
    .get("/{id}/orders")
      .param()
	.name("verbose")
	.type(RestParamType.query)
	.defaultValue("false")
	.description("Verbose order details")
      .endParam()
        .to("direct:customerOrders")
    .post("/neworder").to("direct:customerNewOrder");

在自定义 HTTP 错误消息中嵌套 JsonParserException

您可能要返回自定义错误消息的常见情形是嵌套 JsonParserException 异常。例如,您可以方便地利用 Camel 异常处理机制来创建自定义 HTTP 错误消息,以及 HTTP 错误代码 400,如下所示:

// Java
onException(JsonParseException.class)
    .handled(true)
    .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400))
    .setHeader(Exchange.CONTENT_TYPE, constant("text/plain"))
    .setBody().constant("Invalid json data");

REST DSL 选项

通常,REST DSL 选项可以直接应用于服务定义的基本部分(即,紧跟在 rest ()之后),如下所示:

rest("/email").consumes("text/plain").produces("text/html")
    .post("/to/{recipient}").to("direct:foo")
    .get("/for/{username}").to("direct:bar");

如果指定选项应用到所有下级动词子句。或者选项可应用于每个单独的 verb 子句,如下所示:

rest("/email")
    .post("/to/{recipient}").consumes("text/plain").produces("text/html").to("direct:foo")
    .get("/for/{username}").consumes("text/plain").produces("text/html").to("direct:bar");

如果指定选项只应用到相关 verb 子句,覆盖了基础部分中的任何设置。

表 4.1 “REST DSL 选项” 总结 REST DSL 支持的选项。

表 4.1. REST DSL 选项

Java DSLXML DSL描述

bindingMode()

@bindingMode

指定绑定模式,可用于向 Java 对象(可选)向传出消息提供传入消息。可以设置以下值: off (默认)、autojsonxmljson_xml

consumes()

@consumes

仅限 动词子句接受 HTTP Request 中指定的互联网媒体类型(MIME 类型)。典型的值为: text/plain,text/http,text/xml,application/json,application/xml.

customId()

@customId

定义 JMX 管理的自定义 ID。

description()

description

记录 REST 服务或动词子。适用于 JMX 管理工具.

enableCORS()

@enableCORS

如果为 true,请在 HTTP 响应中启用 CORS (跨原始资源共享)标头。默认为 false

id()

@id

定义 REST 服务的唯一 ID,对于 JMX 管理和其他工具定义非常有用。

method()

@method

指定此 verb 子句处理的 HTTP 方法。通常与通用 verb () 关键字一起使用。

outType()

@outType

当启用对象绑定(即启用 bindingMode 选项时),这个选项指定代表 HTTP 响应 消息的 Java 类型。

produces()

生成

仅限 动词子句在 HTTP 响应中仅生成指定的互联网媒体类型(MIME 类型)。典型的值为: text/plain,text/http,text/xml,application/json,application/xml.

type()

@type

当启用对象绑定(即启用 bindingMode 选项时),这个选项指定代表 HTTP Request 消息的 Java 类型。

VerbURIArgument

@uri

指定路径片段或 URI 模板作为操作动词的参数。例如,获取(VerbURIArgument)

BasePathArgument

@path

指定 rest () 关键字(Java DSL)或 rest 元素(XML DSL)中的基本路径。