【问题标题】:Apache Camel REST endpoint not returning the final bodyApache Camel REST 端点未返回最终正文
【发布时间】:2021-04-28 21:20:09
【问题描述】:

我已经声明了一个 REST 端点,它使用 direct 调用另一个路由。在第二条路线的最后,我正在记录正文,但它与返回浏览器的正文不同。

这是一个重现该行为的小示例(我正在使用带有 Spring Boot 的 Apache Camel):

@Component
public class EntidadeRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        restConfiguration().bindingMode(json);

        rest("/entidade")
            .get("/{id}")
                .to("direct:search-by-id");

        from("direct:search-by-id")
            .routeId("search-by-id")
            .setProperty("idEntidade", simple("${header.id}"))
            .pollEnrich("file:files/mock/dados?noop=true&idempotent=false")
            .unmarshal().json(JsonLibrary.Jackson)
            .split(jsonpath("$"))
            .filter(jsonpath("$[?(@.id == ${property.idEntidade})]"))
            .marshal().json(JsonLibrary.Jackson)
            .log("${body}");
    }
}

我从浏览器调用它,使用 URL: http://localhost:8080/camel/entidade/1 .

在文件夹 files/mock/dados 我有一个名为 entidades.json 的文件,其中有一个 JSON 数组(见下文)。

我知道拆分和过滤器正在工作,因为我在最后一行代码中记录了正文,这就是日志中显示的内容:

2021-04-28 18:15:15.707 INFO 3905542 --- [nio-8080-exec-1] search-by-id : {"id":"1","tipo":"PF", "nome":"若昂玛丽亚"}

但这是返回给浏览器的内容(entidades.json 文件的确切内容):

[{"id":"1","tipo":"PF","nome":"João Maria"},{"id":"2","tipo":"PF","nome ":"Maria João"},{"id":"3","tipo":"PF","nome":"João Silva"},{"id":"4","tipo":"PF ","nome":"何塞·索萨"}]

为什么记录的正文与浏览器中显示的不同并修复它?

PS:如果我删除那些 marshal 和 unmarshal 调用,我会在浏览器中收到以下错误:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException:没有找到类 org.apache.camel.component.file.FileBinding 的序列化程序,也没有发现创建 BeanSerializer 的属性(为避免异常,禁用 SerializationFeature.FAIL_ON_EMPTY_BEANS)(通过参考链:org.apache.camel.component.file.GenericFile["binding"])

【问题讨论】:

  • 您在浏览器中使用的 URL 路径 是什么?您是否像 GET /entidade/2 中一样指定了 {id}
  • @hc_dev 是的,我做到了。我已经更新了问题以澄清。

标签: java spring-boot jackson apache-camel


【解决方案1】:

如果没有输入文件并且不知道您调用的具体 URI(包括路径变量 {id} 的给定值),我只能怀疑以下一些问题:

  • 您是否在 GET 调用结束时提供了 id
  • 为什么要转换 JSON?
  • 您是否测试了正确的拆分?你又聚合了吗?
  • 您要记录每条消息吗?

REST 端点

您将端点指定为GET /entidada/{id}{id} 是一个路径变量。

假设您使用 1 作为 ID 调用 GET /entidata/1。 然后你的 JSON 文件被轮询(读取),解组到...?

JSON 解组/编组

这些 unmarshalmarshal 方法要么用于在不同的数据格式之间进行转换,要么用于在内部使用时从数据表示转换为 Java 对象 (POJO)(例如传递给处理器 Bean 等) .

我想文件 dados 包含 JSON 格式的文本数据。 因此,您可以简单地将此文本读入(文本!)消息正文(如文本消息,比较 JMS 等)并使用它:(a)按 JSON 路径拆分,(b)按 JSON 路径过滤, (c) 记录此 JSON 结果,(d) 将其作为 HTTP 响应发送回调用客户端(例如您的浏览器)。

拆分后会发生什么?

在此之后您尝试拆分(假设您有一个 JSON 数组):

//  incoming:
//  a single enriched message, in body: JSON-array with 4 elements 
.split(jsonpath("$")) // what do you expect as output ?
// here the split usually should be ended using `.end` DSL method
.filter(jsonpath("$[?(@.id == ${property.idEntidade})]")) // each array element of JSON body matched against id, e.g. 1

我将您的 HTTP 响应(包含 4 个人的 JSON 数组)填写到在线 JSON-path evaluator$ 的评估不是拆分,而是单个元素(在结果数组内):完全是原始数组(有 4 人)。


  • 为什么不将该文件拆分为 4 条消息,每条消息都包含一个人员对象?
  • 因为您的 JSON 路径 $ 仅表示 根元素

另外:通常在.split() 之后有一个.end() 再次聚合它们。

你漏掉了。我想这也是一个问题。


过滤器按预期工作

稍后您根据 REST 给定的 id 进行过滤:

.filter(jsonpath("$[?(@.id == ${property.idEntidade})]"))`

这导致记录的元素:

{"id":"1","tipo":"PF","nome":"João Maria"}

过滤器工作成功:只留下一个ID为1的过滤器。

登录骆驼

记录 EIP

.log() 添加到路由链时,这意味着您正在使用Log EIP。在其文档中给出了警告提示

使用流式消息记录消息正文:

如果消息体是基于流的,那么记录消息体可能会导致消息体之后为空。看到这个FAQ。对于流式消息,您可以使用流缓存来记录消息正文,并在之后再次读取消息正文。

因此,当使用它来记录基于流的消息体时,您的空日志消息可能是由副作用引起的。

日志方法的区别

Difference between log in the DSL and Log component部分中解释:

日志 DSL 更轻量级,用于记录人工日志,例如 Starting to do …​ 等。

以下示例(根据您的 REST 路由调整)说明了它的用法:

rest("/entidade")
    .get("/{id}")
    .log("Processing ${id}")
    .to("bean:foo");

日志组件

我建议使用标准的Log 组件,只需使用.to() DSL 传递基于架构log: 的URI 字符串以及所需的参数loggerName

.to("log:filtered-json")

这里 Log 组件的 URI 前缀是 log:。使用 loggerName filtered-json 记录流的每条消息。

【讨论】:

  • 非常感谢,您让我发现了错误。我没有汇总拆分的消息。我将在答案中发布固定代码,但将您的代码标记为已接受。关于您的问题,我已经更新了帖子以澄清。哦,感谢您对日志的解释,即使在我修复了拆分之后,我也没有得到想要的结果,因为我正在记录正文。
【解决方案2】:

错误是我需要将AggregationStrategy 传递给split。我还需要停止记录正文,因为它正在消耗 InputStream。之后,我可以安全地删除那些 marshal 和 unmarshal 调用。

这是最终代码:

@Component
public class EntidadeRoute extends RouteBuilder {

    @Override
    public void configure() throws Exception {
        restConfiguration().bindingMode(json);

        rest("/entidade")
            .get("/{id}")
                .to("direct:search-by-id");

        from("direct:search-by-id")
            .routeId("search-by-id")
            .setProperty("idEntidade", simple("${header.id}"))
            .pollEnrich("file:files/mock/dados?noop=true&idempotent=false")
            .split(jsonpath("$"), takeFirst(Exchange.FILTER_MATCHED))
                .filter(jsonpath("$[?(@.id == ${property.idEntidade})]")).stop()
            .end();
    }

    private AggregationStrategy takeFirst(String matchProperty) {
        return ((oldExchange, newExchange) -> {
            if (oldExchange == null && newExchange.getProperty(matchProperty, Boolean.class)) {
                oldExchange = newExchange;
            }
            return oldExchange;
        });
    }
}

【讨论】:

  • 很好的答案?️。路线看起来很干净(可读代码)。我喜欢你聚合器 takeFirst ?️ 的描述性名称和正文。
猜你喜欢
  • 1970-01-01
  • 2020-02-25
  • 1970-01-01
  • 2015-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-04
  • 1970-01-01
相关资源
最近更新 更多