4.2. 使用 REST DSL 定义服务
REST DSL 是一个 facade
REST DSL 实际上是一个 facade,它为在 Java DSL 或 XML DSL (域特定语言)中定义 REST 服务提供了简化的语法。REST DSL 并不提供 REST 实施,它只是围绕 现有 REST 实施的打包程序(在 Apache Camel 中有多个)。
REST DSL 的优点
REST DSL 打包程序层提供以下优点:
- 现代易用的语法用于定义 REST 服务。
- 与多个不同的 Apache Camel 组件兼容。
-
Swagger 集成(通过
camel-swagger
组件)。
与 REST DSL 集成的组件
由于 REST DSL 不是实际的 REST 实施,因此您需要做的第一件事是选择 Camel 组件以提供底层实施。以下 Camel 组件目前与 REST DSL 集成:
-
Servlet 组件(
camel-servlet
)。 -
spark REST 组件(
camel-spark-rest
)。 -
Netty4 HTTP 组件(
camel-netty4-http
)。 -
jetty 组件(
camel-jetty
)。 -
https://access.redhat.com/documentation/zh-cn/red_hat_fuse/7.5/html-single/apache_camel_component_reference/index#restlet-component 组件(
camel-restlet
)。
Rest 组件( camel-core
的一部分)不是 REST 实施。与 REST DSL 一样,Rest 组件是一个 facade,它提供了一个简化的语法,以使用 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 路径片段的 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
基本路径是可选的。如果您希望,您可以在每个动词 子句中指定完整路径:
rest() .get("/say/hello").to("direct:hello") .get("/say/bye").to("direct:bye");
使用 Dynamic To
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 模板
在操作动词参数中,您可以指定一个 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}
路径片段的文本,并将这个捕获的文本复制到 名称
消息标头中。如果您通过发送 GET HTTP 请求并使用以 /say/hello/Joe
结尾的 URL 调用该服务,则 HTTP 响应为 Hello Joe
。
嵌入式路由语法
您可以选择使用 route
关键字(Java DSL)或 route 元素(XML DSL) ()
来
终止操作句子,而是使用 route ()关键字(Java DSL)或 route
元素(XML DSL)来直接将 Apache Camel 路由嵌入到 REST DSL 中。route ()
关键字可让您将路由嵌入到 verb 子句中,语法如下:
RESTVerbClause.route("...").CamelRoute.endRest()
其中 endRest ()
关键字(仅限 Java DSL 关键字)是必要的标点标记,使您能够分隔 verb 子句(当 rest ()
构建器中有多个动词子时)。
例如,您可以重构 Hello World 示例以使用嵌入式 Camel 路由,如下所示:
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 ()
)或拦截器(使用 intercept ()
),则这些 exception 子句和拦截器也会在嵌入式路由中活跃。
REST DSL 和 HTTP 传输组件
如果您没有显式配置 HTTP 传输组件,则 REST DSL 通过检查类路径上的可用组件来自动发现要使用的 HTTP 组件。REST DSL 会查找任何 HTTP 组件的默认名称,并使用找到的第一个名称。如果 classpath 中没有 HTTP 组件,且您没有显式配置 HTTP 传输,则默认的 HTTP 组件为 camel-http
。
指定请求和响应的内容类型
您可以使用 consume ()和 generate
选项过滤 HTTP 请求和响应 的内容类型,或者在 XML 中消耗 ()
和生成
属性。例如,一些常见的内容类型(官方称为 互联网介质类型)如下:
-
text/plain
-
text/html
-
text/xml
-
application/json
-
application/xml
内容类型在 REST DSL 中的 verb 子句上作为选项指定。例如,要将 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>
您还可以将参数指定为 consume ()
或 produce ()
作为以逗号分隔的列表。例如,consume ("text/plain, application/json")
。
其他 HTTP 方法
有些 HTTP 服务器实现支持额外的 HTTP 方法,这些方法不是由 REST DSL、get ()
、head ()
、put ()
、post ()
、delete ()
、patch ()
中的标准动词集提供。要访问其他 HTTP 方法,您可以在 XML DSL 中使用 generic 关键字 verb ()
和 generic 元素 verb
动词。
例如,要在 Java 中实施 TRACE HTTP 方法:
rest("/say") .verb("TRACE", "/hello").route().transform();
其中 transformation ()
将 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
标头键设置为错误代码值来指定 HTTP 错误代码(如400
、404
等等)。此设置指示您要发送错误消息回复的 REST DSL,而不是常规响应。 - 在消息正文中填充您的自定义错误消息。
-
如果需要,设置
Content-Type
标头。 如果您的 REST 服务被配置为 marshal 到 Java 对象(即启用
bindingMode
),您应该确保启用skipBindingOnErrorCode
选项(默认为 )。这是为了确保 REST DSL 在发送响应时不会尝试 unmarshal 消息正文。有关对象绑定的详情,请参阅 第 4.3 节 “Marshalling to and from Java Objects”。
以下 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 请求中接受指定的 Internet 介质类型(MIME 类型)。典型的值有: |
|
| 为 JMX 管理定义自定义 ID。 |
|
| 记录 REST 服务或操作动词子句。适用于 JMX 管理和工具。 |
|
|
如果为 |
|
| 为 REST 服务定义唯一 ID,这对于定义 JMX 管理和其他工具非常有用。 |
|
|
指定此 verb 子句处理的 HTTP 方法。通常与通用 |
|
|
当对象绑定被启用(即启用 |
|
|
限制 verb 子句,以仅在 HTTP 响应中生成指定的 Internet 介质类型(MIME 类型)。典型的值有: |
|
|
当启用对象绑定(即启用 |
|
|
指定路径片段或 URI 模板作为操作动词的参数。例如, |
|
|
指定 |