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 集成:
-
Servlet 组件(
camel-servlet
)。 -
spark REST 组件(
camel-spark-rest
)。 -
Netty4 HTTP 组件(
camel-netty4-http
)。 -
jetty component (
camel-jetty
)。 -
restlet 组件(
camel-restlet
)。 -
Undertow 组件(
camel-undertow
)。
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 中的 builder 表达式:
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 路径片段的一个或多个 verb 子句。HTTP 动词可以是 get ()
、head ()
、put ()
、post ()
、delete ()
、patch ()
或 verb ()
之一。每个 verb 子句都可以使用以下任何一种语法:
动词子句以
to ()
关键字结尾。例如:get("...").Option()+.to("...")
verb 子句以
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 定义服务。每个 verb 子句都由一个
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
动态 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
dynamic to 参数的详情,请参考 “动态到”一节。
URI 模板
在 verb 参数中,您可以指定一个 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
结尾的 GET HTTP Request 来调用该服务,HTTP 响应是 Hello Joe
。
嵌入式路由语法
您可以使用 route
关键字(Java DSL)或 ()
to
element (XML DSL)终止 verb 子句,而是使用 route ()关键字(Java DSL)或 route
元素(XML DSL)直接嵌入 Apache Camel 路由。route ()
关键字可让您使用以下语法将路由嵌入到 verb 子句中:
RESTVerbClause.route("...").CamelRoute.endRest()
其中 endRest ()
关键字(Java DSL)是一个必要的标点标记,可让您分隔 verb 子句(当 rest ()
构建器中有多个 verb 子句)。
例如,您可以重构 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
中定义任何异常子句(使用 Exception ()
)或拦截器(使用 intercept ()
),则这些 exception 子句和拦截器也在嵌入的路由中处于活跃状态。
REST DSL 和 HTTP 传输组件
如果您没有显式配置 HTTP 传输组件,则 REST DSL 会通过检查 classpath 上的可用组件来自动发现要使用的 HTTP 组件。REST DSL 查找任何 HTTP 组件的默认名称,并使用它找到的第一个名称。如果 classpath 上没有 HTTP 组件,且您没有明确配置 HTTP 传输,则默认的 HTTP 组件为 camel-http
。
指定请求和响应的内容类型
您可以使用 Java 中的 consumes ()
和 generate ()
选项过滤 HTTP 请求和响应 的内容类型,或者在 XML 中生成
属性。例如,一些通用内容类型(官方称为 互联网介质类型)如下:
-
text/plain
-
text/html
-
text/xml
-
application/json
-
application/xml
内容类型被指定为 REST DSL 中 verb 子句上的选项。例如,要将 verb 子句限制为仅接受 text/plain
HTTP 请求,并且仅发送 文本/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>
您还可以指定要以逗号分隔的列表 形式使用
generate ()的参数。例如,()
或使用("text/plain, application/json")
。
其他 HTTP 方法
有些 HTTP 服务器实现支持额外的 HTTP 方法,它们不是由 REST DSL, get ()
, head ()
, put ()
, post (),
的标准集合提供。要访问额外的 HTTP 方法,您可以在 Java DSL 中使用 generic 关键字 delete (),
patch ()
verb ()
,并在 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 错误消息:
-
通过将
Exchange.HTTP_RESPONSE_CODE
标头键设置为错误代码值(如400
、404
等等)来指定 HTTP 错误代码。此设置指示您要发送错误消息回复的 REST DSL,而不是常规响应。 - 使用自定义错误消息填充消息正文。
-
如果需要,设置
Content-Type
标头。 如果您的 REST 服务被配置为 marshal 到 Java 对象(已启用
bindingMode
),您应该确保启用了skipBindingOnErrorCode
选项(默认为 )。这是为了确保 REST DSL 在发送响应时不会尝试 unmarshal 消息正文。有关对象绑定的详情,请参阅 第 4.3 节 “到 Java 对象和从 Java 对象进行复制”。
以下 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 支持的选项。
Java DSL | XML DSL | 描述 |
---|---|---|
|
|
指定绑定模式,可用于将传入消息发送到 Java 对象(以及可选的 unmarshal Java 对象到传出消息)。可以有以下值: |
|
|
将 verb 子句限制为仅接受 HTTP Request 中指定的互联网介质类型(MIME 类型)。典型值有: |
|
| 定义用于 JMX 管理的自定义 ID。 |
|
| 记录 REST 服务或 verb 子句。适用于 JMX 管理和工具。 |
|
|
如果为 |
|
| 为 REST 服务定义唯一 ID,这对于为 JMX 管理和其他工具定义非常有用。 |
|
|
指定此 verb 子句处理的 HTTP 方法。通常与 generic |
|
|
启用对象绑定(即启用 |
|
|
将 verb 子句限制为仅在 HTTP 响应中生成指定的互联网介质类型(MIME 类型)。典型值有: |
|
|
启用对象绑定(即启用 |
|
|
指定路径片段或 URI 模板作为操作动词的参数。例如, |
|
|
指定 |