使用 REST 和 Rest DSL

Rest DSL 错误处理与验证

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

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

Rest DSL - 错误处理与验证

返回 Rest DSL

原样定义自定义错误消息

如果你想定义自定义错误消息,并连同 HTTP 错误码(例如 400、404 等)一起返回给客户端,那么你需要将键为 Exchange.HTTP_RESPONSE_CODE 的消息头设置为该错误码(必须是 300 以上,例如 404)。然后在消息正文中填入回复消息内容,并可选择性地设置 content-type 消息头。下面给出一个简单的示例:

  • Java
  • XML
  • YAML
restConfiguration().component("netty-http").host("localhost").port(9091).bindingMode(RestBindingMode.json);
// use the rest DSL to define the rest services
rest("/users/")
    .post("lives").type(UserPojo.class).outType(CountryPojo.class)
    .to("direct:users-lives");

from("direct:users-lives")
    .choice()
        .when().simple("${body.id} < 100")
            .bean("userErrorService", "idToLowError")
        .otherwise()
            .bean("userService", "livesWhere");
<restConfiguration component="netty-http" host="localhost" port="9091" bindingMode="json"/>

<rest path="/users/">
    <post path="lives" type="com.foo.UserPojo" outType="com.foo.CountryPojo">
        <to uri="direct:users-lives"/>
    </post>
</rest>

<route>
    <from uri="direct:users-lives"/>
    <choice>
        <when>
            <simple>${body.id} &lt; 100</simple>
            <bean ref="userErrorService" method="idToLowError"/>
        </when>
        <otherwise>
            <bean ref="userService" method="livesWhere"/>
        </otherwise>
    </choice>
</route>
- restConfiguration:
    component: "netty-http"
    host: "localhost"
    port: "9091"
    bindingMode: "json"
- rest:
    path: "/users"
    post:
      - path: "/lives"
        to: "direct:users-lives"
        type: "com.foo.UserPojo"
        outType: "com.foo.CountryPojo"
- route:
    from:
      uri: direct:users-lives
      steps:
        - choice:
            when:
              - expression:
                  simple:
                    expression: "${body.id} < 100"
                steps:
                  - bean:
                      ref: userErrorService
                      method: idToLowError
            otherwise:
              steps:
                - bean:
                    ref: userService
                    method: livesWhere

在本示例中,如果输入的 id 是一个小于 100 的数字,我们希望使用 UserErrorService bean 返回一条自定义错误消息,该 bean 的实现如下:

public class UserErrorService {
    public void idToLowError(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。这一点很重要,因为它告诉 rest-dsl 这是一条自定义错误消息,该消息不应使用输出 POJO 绑定(否则会绑定到 CountryPojo)。

捕获 JsonParserException 并返回自定义错误消息

你可以原样返回自定义消息(参见上一节)。因此,我们可以结合 Camel 错误处理器来捕获 JsonParserException,处理该异常并构建自定义响应消息。例如,要返回 HTTP 错误码 400 和一条硬编码的消息,可以按如下方式操作:

  • Java
  • XML
  • YAML
onException(JsonParseException.class)
    .handled(true)
    .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(400))
    .setHeader(Exchange.CONTENT_TYPE, constant("text/plain"))
    .setBody().constant("Invalid json data");
<onException>
    <exception>com.fasterxml.jackson.core.JsonParseException</exception>
    <handled>
        <constant>true</constant>
    </handled>
    <setHeader name="CamelHttpResponseCode">
        <constant>400</constant>
    </setHeader>
    <setHeader name="Content-Type">
        <constant>text/plain</constant>
    </setHeader>
    <setBody>
        <constant>Invalid json data</constant>
    </setBody>
</onException>
- onException:
    exception:
      - com.fasterxml.jackson.core.JsonParseException
    handled:
      constant:
        expression: "true"
    steps:
      - setHeader:
          name: CamelHttpResponseCode
          expression:
            constant:
              expression: 400
      - setHeader:
          name: Content-Type
          expression:
            constant:
              expression: text/plain
      - setBody:
          expression:
            constant:
              expression: Invalid json data

查询/请求头参数的默认值

你可以在 rest-dsl 中为参数指定默认值,例如下面的 verbose 参数:

  • Java
  • XML
  • YAML
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");
<rest path="/customers/">
    <get path="/{id}">
        <to uri="direct:customerDetail"/>
    </get>
    <get path="/{id}/orders">
        <param description="Verbose order details" name="verbose" type="query" defaultValue="false"/>
        <to uri="direct:customerOrders"/>
    </get>
    <post path="/neworder">
        <to uri="direct:customerNewOrder"/>
    </post>
</rest>
- rest:
    path: "/customers/"
    get:
      - path: "/{id}"
        to: "direct:customerDetail"
      - path: "/{id}/orders"
        to: "direct:customerOrders"
        param:
          - name: "verbose"
            type: "query"
            defaultValue: "false"
            description: "Verbose order details"
    post:
      - path: "/neworder"
        to: "direct:customerNewOrder"

默认值会被自动设置为传入 Camel Message 的头部。因此,如果调用 /customers/id/orders 时没有包含键为 verbose 的查询参数,Camel 就会因为该参数声明了默认值,而添加一个键为 verbose、值为 false 的头部。此功能仅适用于查询参数。请求头也可以用同样的方式设置默认值。

  • Java
  • XML
  • YAML
rest("/customers/")
    .get("/{id}").to("direct:customerDetail")
    .get("/{id}/orders")
      .param().name("indicator").type(RestParamType.header).defaultValue("disabled").description("Feature Enabled Indicator").endParam()
        .to("direct:customerOrders")
    .post("/neworder").to("direct:customerNewOrder");
<rest path="/customers/">
    <get path="/{id}">
        <param name="id" type="path"/>
        <to uri="direct:customerDetail"/>
    </get>
    <get path="/{id}/orders">
        <param description="Feature Enabled Indicator" name="indicator" type="header" defaultValue="disabled"/>
        <to uri="direct:customerOrders"/>
    </get>
    <post path="/neworder">
        <to uri="direct:customerNewOrder"/>
    </post>
</rest>
- rest:
    path: "/customers/"
    get:
      - path: "/{id}"
        to: "direct:customerDetail"
      - path: "/{id}/orders"
        to: "direct:customerOrders"
        param:
          - name: "indicator"
            type: "header"
            defaultValue: "disabled"
            description: "Feature Enabled Indicator"
    post:
      - path: "/neworder"
        to: "direct:customerNewOrder"

客户端请求与响应校验

可以启用对客户端传入请求的校验。该校验会检查以下内容:

  • Content-Type 请求头是否与 Rest DSL 所消费的类型匹配。(返回 HTTP 状态码 415)
  • Accept 请求头是否与 Rest DSL 所产生的类型匹配。(返回 HTTP 状态码 406)
  • 是否缺少必需的数据(查询参数、HTTP 请求头、消息体)。(返回 HTTP 状态码 400)
  • 检查查询参数或 HTTP 请求头是否包含不被允许的取值。(返回 HTTP 状态码 400)
  • 消息体解析错误(需要启用 JSON、XML 或自动绑定模式)。(返回 HTTP 状态码 400)

如果校验失败,Rest DSL 将返回一个带有 HTTP 错误码的响应。

该校验默认是关闭的(以保持向后兼容)。可以通过 clientRequestValidation 开启,示例如下:

  • Java
  • XML
  • YAML
restConfiguration().component("jetty").host("localhost")
    .clientRequestValidation(true);
<restConfiguration component="jetty" host="localhost" clientRequestValidation="true"/>
- restConfiguration:
    component: "jetty"
    host: "localhost"
    clientRequestValidation: "true"

验证器是可插拔的,Camel 开箱即用地提供了默认实现。

不过,camel-openapi-validator 在客户端请求校验方面使用的是第三方库 Atlassian Swagger Request Validator。该库比 camel-core 中的默认验证器功能更强大,例如它可以校验负载是否按照 OpenAPI 规范的结构组织。

在 Camel 4.13 中,我们还新增了响应验证器,它更像是一种开发辅助工具,可以在构建 Camel 集成时启用,用于帮助确保 Camel 返回给 HTTP 客户端的内容是有效的。响应验证器会检查以下内容:

  • 状态码和 Content-Type 是否与 Rest DSL 的响应消息匹配。
  • 根据 Rest DSL 响应消息头,检查是否包含了预期的响应头。
  • 如果响应体是 JSON,则检查其是否为有效的 JSON。

一旦检测到任何错误,将返回 HTTP 状态码 500。

此外,可以将 camel-openapi-validator 加入 classpath,以获得更强大的响应验证器,用它来校验响应负载是否按照 OpenAPI 规范的结构组织。

评论

登录后参与评论

正在加载评论…