操作指南

使用 REST 和 Rest DSL

师成师成· 更新于 2026-09-28· 阅读 27 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

Rest DSL

Apache Camel 提供了一种 REST 风格的 DSL。

其目的是让最终用户能够以 REST 风格(使用 get、post、delete 等动词)来定义由 Camel 托管的 REST 服务。

从 Camel 4.6 开始,Rest DSL 引入了使用原生 OpenAPI 规范文件的契约优先(contract-first)方式,功能得到了增强。相关内容记录在 Rest DSL with OpenAPI contract first 页面中。当前页面介绍的是 Camel 长期以来提供的代码优先(code-first)Rest DSL。

工作原理

Rest DSL 是一个门面(facade),它构建 Rest 端点,作为 Camel 路由的消费者。实际的 REST 传输则由支持原生 REST 集成的 Camel REST 组件来承担,例如 Netty HTTP、Servlet 等。

支持 Rest DSL 的组件

以下 Camel 组件支持 Rest DSL:

Rest DSL

让我们定义一个具有以下 URL 映射的 REST 服务:

基础路径URI 模板动词消费类型(Consumes)
/say/helloget全部
/say/byegetapplication/json
/say/byepost全部
  • Java
  • XML
  • YAML

要在 Java DSL 中使用 Rest DSL,只需像编写常规 Camel 路由一样,继承 RouteBuilder 并在 configure 方法中定义路由即可。

一个简单的 REST 服务可以如下定义,我们使用 rest() 来定义服务,如下面所示:

@Override
public void configure() throws Exception {
    rest("/say")
        .get("/hello").to("direct:hello")
        .get("/bye").consumes("application/json").to("direct:bye")
        .post("/bye").to("mock:update");

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

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

注意,在 REST 服务中,我们使用 to() 直接路由到一个 Camel 端点。这是因为 Rest DSL 提供了一种简写方式,可以直接使用 to() 路由到端点。

一个简单的 REST 服务可以如下定义,我们使用 <rest> 来定义这些服务:

  <rest path="/say">
    <get path="/hello">
      <to uri="direct:hello"/>
    </get>
    <get path="/bye" consumes="application/json">
      <to uri="direct:bye"/>
    </get>
    <post path="/bye">
      <to uri="mock:update"/>
    </post>
  </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>

一个简单的 REST 服务可以按如下方式定义,其中我们使用 - rest: 来定义服务,如下所示:

- rest:
    path: "/say"
    get:
      - path: "/hello"
        to: "direct:hello"
      - path: "/bye"
        consumes: "application/json"
        to: "direct:bye"
    post:
      - path: "/bye"
        to: "direct:update"
- route:
    from:
      uri: direct:hello
      steps:
        - transform:
            expression:
              constant:
                expression: Hello World
- route:
    from:
      uri: direct:bye
      steps:
        - transform:
            expression:
              constant:
                expression: Bye World

使用基础路径

REST DSL 允许定义基础路径,以帮助践行*"不要重复自己"*(DRY)原则。例如,要定义客户相关的路径,我们可以在 rest("/customer") 中设置基础路径,然后在各个动词中提供 uri 模板,如下所示:

  • Java
  • XML
  • YAML
rest("/customers/")
    .get("/{id}").to("direct:customerDetail")
    .get("/{id}/orders").to("direct:customerOrders")
    .post("/neworder").to("direct:customerNewOrder");
<rest path="/customers/">
  <get path="/{id}">
    <to uri="direct:customerDetail"/>
  </get>
  <get path="/{id}/orders">
    <to uri="direct:customerOrders"/>
  </get>
  <post path="/neworder">
    <to uri="direct:customerNewOrder"/>
  </post>
</rest>
- rest:
    path: "/customers/"
    get:
      - path: "/{id}}"
        to: "direct:customerDetails"
      - path: "/{id}/orders}"
        to: "direct:customerOrders"
    post:
      - path: "/neworder"
        to: "direct:customerNewOrder"
Rest DSL 会处理使用基础路径(base path)和 URI 模板时出现的重复路径分隔符。在上面的示例中,rest 基础路径以斜杠 / 结尾,而动词以斜杠 / 开头。Camel 会处理这种情况,去掉重复的斜杠。

基础路径和 URI 模板并非都必须使用。你可以省略基础路径,只在各个动词中定义基础路径和 URI 模板。上面的示例可以定义为:

  • Java
  • XML
  • YAML
rest()
    .get("/customers/{id}").to("direct:customerDetail")
    .get("/customers/{id}/orders").to("direct:customerOrders")
    .post("/customers/neworder").to("direct:customerNewOrder");
<rest>
  <get path="/customers/{id}">
    <to uri="direct:customerDetail"/>
  </get>
  <get path="/customers/{id}/orders">
    <to uri="direct:customerOrders"/>
  </get>
  <post path="/customers/neworder">
    <to uri="direct:customerNewOrder"/>
  </post>
</rest>
- rest:
    get:
      - path: "/customers/{id}}"
        to: "direct:customerDetails"
      - path: "/customers/{id}/orders}"
        to: "direct:customerOrders"
    post:
      - path: "/customers/neworder"
        to: "direct:customerNewOrder"

你可以组合路径参数来构建复杂的表达式。

例如,下面的定义使用类似文件语法的方式来获取某个条目:

  • Java
  • XML
  • YAML
 rest("items/")
     .get("{id}/{filename}.{content-type}")
     .to("direct:item");
<rest path="items/">
    <get path="{id}/{filename}.{content-type}">
        <to uri="direct:item"/>
    </get>
</rest>
- rest:
    path: "items/"
    get:
      - path: "{id}/{filename}.{content-type}"
        to: "direct:item"

管理 Rest 服务

每个 Rest 服务都会变成一个 Camel 路由,因此在第一个示例中,我们有 2 个 get 和 1 个 post REST 服务,它们各自都会成为一个 Camel 路由。

这样,Apache Camel 管理和运行这些服务的方式就完全相同,因为它们只是普通的 Camel 路由。这意味着当今任何处理 Camel 路由的工具和 API,同样也适用于这些 REST 服务。

要在 Camel 中使用 JMX,必须在类路径中包含 camel-management JAR。

这意味着你可以使用 JMX 来停止/启动路由,还可以获取路由的 JMX 指标,例如已处理的消息数量及其性能统计数据。

还有一个 Rest Registry JMX MBean,其中包含了所有已定义 REST 服务的注册表。

将 Rest DSL 内联为单一路由

Camel 4.4 及更早版本默认禁用内联路由,Camel 4.5 及更高版本默认启用内联路由。

每个 Rest 服务都会变成一个 Camel 路由,这意味着如果 Rest 服务通过 direct 调用另一个 Camel 路由(这是一种常见的做法),那么每个 Rest 服务就会变成两条路由。如果你有很多 Rest 服务,这会变得更难管理。

当你使用 direct 端点时,可以启用 Rest DSL 自动将 direct 路由内联到 rest 路由中,也就是说每个 Rest 服务只对应一条路由。

使用内联路由时,每个 REST 端点应 1:1 关联到一个唯一的 direct 端点。被关联的 direct 路由会被内联,因此不再作为独立路由存在,也无法从其他常规 Camel 路由中调用。换句话说,被内联的路由实际上已经移入了 rest-dsl 内部,不再以路由的形式存在。更多详情见下文。

要实现这一点,你必须使用 direct 端点,并且每个服务中的端点名称必须唯一。同时必须启用 inlineRoutes 选项。

例如,在下面的 Java DSL 中,我们启用了内联路由,每个 Rest 服务都使用具有唯一名称的 direct 端点。

  • Java
  • XML
  • YAML
restConfiguration().inlineRoutes(true);

rest("/customers/")
    .get("/{id}").to("direct:customerDetail")
    .get("/{id}/orders").to("direct:customerOrders")
    .post("/neworder").to("direct:customerNewOrder");
<restConfiguration inlineRoutes="true"/>

<rest>
  <get path="/customers/{id}">
    <to uri="direct:customerDetail"/>
  </get>
  <get path="/customers/{id}/orders">
    <to uri="direct:customerOrders"/>
  </get>
  <post path="/customers/neworder">
    <to uri="direct:customerNewOrder"/>
  </post>
</rest>
- restConfiguration:
    inlineRoutes: true
- rest:
    get:
      - path: "/customers/{id}}"
        to: "direct:customerDetails"
      - path: "/customers/{id}/orders}"
        to: "direct:customerOrders"
    post:
      - path: "/customers/neworder"
        to: "direct:customerNewOrder"

如果你使用 Camel Main、Camel Spring Boot、Camel Quarkus 或 Camel CLI,也可以在 application.properties 中启用该功能,例如:

camel.rest.inline-routes = true

注意,上面的每个 REST 服务都各自使用一个唯一的、与之 1:1 绑定的 direct 端点(direct:customerDetail、direct:customerOrders、direct:customerNewOrder)。这意味着你无法从其他路由调用这些路由,例如下面这样是无法正常工作的:

  • Java
  • XML
  • YAML
from("kafka:new-order")
   .to("direct:customerNewOrder");
<route>
    <from uri="kafka:new-order"/>
    <to uri="direct:customerNewOrder"/>
</route>
- route:
    from:
      uri: kafka:new-order
      steps:
        - to:
            uri: direct:customerNewOrder

因此,如果你希望既能从 Rest DSL 又能从其他常规 Camel 路由中调用公共路由,请将这些路由拆分为独立的路由,如下所示:

  • Java
  • XML
  • YAML
restConfiguration().inlineRoutes(true);

rest("/customers/")
    .get("/{id}").to("direct:customerDetail")
    .get("/{id}/orders").to("direct:customerOrders")
    .post("/neworder").to("direct:customerNewOrder");

from("direct:customerNewOrder")
  // do some stuff here
  .to("direct:commonCustomerNewOrder"); // call common route

from("direct:commonCustomerNewOrder")
  // do stuff here
  .log("Created new order");

from("kafka:new-order")
   .to("direct:commonCustomerNewOrder"); // make sure to call the common route
<restConfiguration inlineRoutes="true"/>

<rest path="/customers/">
    <get path="/{id}">
        <to uri="direct:customerDetail"/>
    </get>
    <get path="/{id}/orders">
        <to uri="direct:customerOrders"/>
    </get>
    <post path="/neworder">
        <to uri="direct:customerNewOrder"/>
    </post>
</rest>

<routes>
    <route>
        <from uri="direct:customerNewOrder"/>
        <to uri="direct:commonCustomerNewOrder"/>
    </route>
    <route>
        <from uri="direct:commonCustomerNewOrder"/>
        <log message="Created new order"/>
    </route>
    <route>
        <from uri="kafka:new-order"/>
        <to uri="direct:commonCustomerNewOrder"/>
    </route>
</routes>
- restConfiguration:
    inlineRoutes: true
- rest:
    path: "/customers/"
    get:
      - path: "/{id}}"
        to: "direct:customerDetails"
      - path: "/{id}/orders}"
        to: "direct:customerOrders"
    post:
      - path: "/neworder"
        to: "direct:customerNewOrder"
- route:
    from:
      uri: direct:customerNewOrder
      steps:
        - to:
            uri: direct:commonCustomerNewOrder
- route:
    from:
      uri: direct:commonCustomerNewOrder
      steps:
        - log:
            message: Created new order
- route:
    from:
      uri: kafka:new-order
      steps:
        - to:
            uri: direct:commonCustomerNewOrder

注意公共共享路由是如何被拆分为 direct:commonCustomerNewOrder 路由的。它既可以从 Rest DSL 调用,也可以从常规 Camel 路由调用。

禁用 REST 服务

在使用 Rest DSL 开发 REST 服务时,你可能希望临时禁用某些 REST 端点,这可以通过 disabled 来实现,如下所示。

  • Java
  • XML
  • YAML
rest("/customers/")
    .get("/{id}").to("direct:customerDetail")
    .get("/{id}/orders").to("direct:customerOrders").disabled("{{ordersEnabled}}")
    .post("/neworder").to("direct:customerNewOrder").disabled();
<rest>
  <get path="/customers/{id}">
    <to uri="direct:customerDetail"/>
  </get>
  <get path="/customers/{id}/orders" disabled="{{ordersEnabled}}">
    <to uri="direct:customerOrders"/>
  </get>
  <post path="/customers/neworder" disabled="true">
    <to uri="direct:customerNewOrder"/>
  </post>
</rest>
- rest:
    get:
      - path: "/customers/{id}}"
        to: "direct:customerDetails"
      - path: "/customers/{id}/orders}"
        to: "direct:customerOrders"
        disabled: "{{ordersEnabled}}"
    post:
      - path: "/customers/neworder"
        to: "direct:customerNewOrder"
        disabled: "true"

在此示例中,最后两个 REST 端点通过 disabled 进行配置。你可以使用属性占位符,让外部配置来决定 REST 端点是否被禁用。在此示例中,/customers/{id}/orders 端点通过占位符被禁用,而最后一个 REST 端点则被硬编码为禁用状态。

更多信息

有关特定主题的详细文档,请参阅以下子页面:

评论

登录后参与评论

正在加载评论…