3

我有一个带有 apache camel 的 springboot 应用程序。在其中我有一个骆驼上下文。我正在尝试通过 curl 发送带有密钥对值的 json 并通过路由处理它。

发送数据:

curl --header "Content-Type: application/json" -X POST -d '{"msgId=EB2C7265-EF68-4F8F-A709-BEE2C52E842B", "ticket":"ERR001"}' http://lcalhost:8888/api/erroradmin

骆驼上下文.xml:

<?xml version="1.0" encoding="utf-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd         http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
        <camelContext xmlns="http://camel.apache.org/schema/spring" useMDCLogging="true">
            <properties>
                <property key="CamelLogEipName" value="ThisLogger"/>
            </properties>
            <dataFormats>
                <!-- here we define a Json data format with the id jack and that it should use the TestPojo as the class type when
                     doing unmarshal. The unmarshalTypeName is optional, if not provided Camel will use a Map as the type -->
                <json id="jack" library="Jackson" unmarshalTypeName="java.util.HashMap"/>
            </dataFormats>

            <restConfiguration component="jetty" port="8888" bindingMode="json">
                <dataFormatProperty key="prettyPrint" value="true"/>
            </restConfiguration>

            <rest path="/api/erroradmin">
                <get uri="{id}">
                    <to uri="direct:processErrorAdminGet"/>
                </get>
                <post>
                    <to uri="direct:processErrorAdminPost"/>
                </post>
            </rest>

            <route id="processErrorAdminPost">
                <from uri="direct:processErrorAdminPost"/>
                <log message="Route(processErrorAdminPost): ${body}"/>
                <unmarshal>
                    <custom ref="jack"/>
                </unmarshal>
                <log message="Route(processErrorAdminPost): ${body}"/>

            </route>
        </camelContext>
    </beans>

我得到以下堆栈跟踪:

org.apache.camel.InvalidPayloadException:没有可用类型的主体:java.io.InputStream 但具有值:{msgId=D507B9EE-176D-4F3C-88E7-9E36CC2B9731,ticket=ERR001} 类型:java.util.LinkedHashMap on: HttpMessage@0x28c1a31a。原因:没有类型转换器可用于将类型:java.util.LinkedHashMap 转换为所需的类型:java.io.InputStream,值为 {msgId=D507B9EE-176D-4F3C-88E7-9E36CC2B9731,ticket=ERR001}。交换 [09395660-c947-47f1-b00f-d0d3030a39d1]。原因:[org.apache.camel.NoTypeConversionAvailableException - 没有类型转换器可用于从类型:java.util.LinkedHashMap 转换为所需类型:java.io.InputStream,值为 {msgId=D507B9EE-176D-4F3C-88E7-9E36CC2B9731 , 票=ERR001}]

4

2 回答 2

3

欢迎来到 Stackoverflow!我坚信bindingMode="json"在第 13 行提到的,是这次失败的根本原因。手册说_

使用绑定时,您还必须配置要映射到的 POJO 类型。这对于传入消息是强制性的,对于传出消息是可选的。

我真的很害怕 XML DSL,但是,这里有一个近似等效的 Java 中的 rest DSL。

@Component
@Slf4j
public class MySpringBootRouter extends RouteBuilder {

    @Override
    public void configure() {

        restConfiguration()
                .component("undertow")
                .host("127.0.0.1")
                .port(9090)
                //This works only when a POJO mapping is possible - Ref: https://camel.apache.org/manual/latest/rest-dsl.html
                //<quote>When using binding you must also configure what POJO type to map to. This is mandatory for incoming messages, and optional for outgoing.</quote>
                //.bindingMode(RestBindingMode.json)
                .dataFormatProperty("prettyPrint", "true");


        rest("/api")
                .post("/erroradmin")
                .to("direct:postError")

                .get("/erroradmin/{id}").to("direct:getError");

        from("direct:getError")
                .process(exchange -> {
                    exchange.getMessage().setBody(("{'messageID':'" + UUID.randomUUID().toString() + "','ticketID':'1234'}"));
                });

        from("direct:postError")
                .unmarshal()
                .json(JsonLibrary.Jackson)
                .process(exchange -> {
                    log.info("Type of incoming body:{}", exchange.getIn().getBody().getClass().getName());
                    log.info("Incoming body:{}", exchange.getIn().getBody());
                }).transform().constant("{'httpResponse:200':'OK'}");
    }

}

启动后,我使用 cURL 发送有效负载,如下所示

curl -d '{"msgId":"EB2C7265-EF68-4F8F-A709-BEE2C52E842B", "ticket":"ERR001"}' -H "Content-Type: application/json" -X POST http://localh
ost:9090/api/erroradmin

您会在日志中看到类似下面的内容

2020-02-18 11:44:13.032  INFO 2708 --- [  XNIO-1 task-4] o.a.c.community.so.MySpringBootRouter    : Type of incoming body:java.util.LinkedHashMap
2020-02-18 11:44:13.032  INFO 2708 --- [  XNIO-1 task-4] o.a.c.community.so.MySpringBootRouter    : Incoming body:{msgId=EB2C7265-EF68-4F8F-A709-BEE2C52E842B, ticket=ERR001}

哦,顺便说一句,您原来的 JSON 有效负载格式不正确。整个 Java 项目都可以在这里找到,如果你想玩的话


编辑:附加标头处理步骤

        from("direct:postError")
                .unmarshal()
                .json(JsonLibrary.Jackson)
                .process(exchange -> {
                    LinkedHashMap map = exchange.getIn().getBody(LinkedHashMap.class);
                    map.keySet().stream().forEach( item -> exchange.getIn().setHeader(item.toString(),map.get(item)));
                })
//This step is just to print the Headers. Doesnt do anything useful
                .process( exchange -> {
                    log.info(String.valueOf(exchange.getIn().getHeaders()));
                })
                .transform().constant("{'httpResponse:200':'OK'}");
于 2020-02-18T10:56:26.493 回答
2

经过一段时间的搜索和阅读,我找到了解决方案。

这样做的方法是使用 JsonPath。现在在 Java DSL 中有很多例子,但在 XML DSL 中,例子不多。我终于找到了一个可行的例子。

我的骆驼上下文现在看起来像这样:

<camelContext xmlns="http://camel.apache.org/schema/spring" useMDCLogging="true" streamCache="true">
    <properties>
        <property key="CamelLogEipName" value="SomeLogger"/>
    </properties>

     <dataFormats>
            <json id="json" library="Jackson"/>     
     </dataFormats>

    <restConfiguration component="jetty" port="9090" >
        <dataFormatProperty key="prettyPrint" value="true"/>
    </restConfiguration>

    <rest path="/api/erroradmin">
        <post>
            <to uri="direct:error" />
        </post>
    </rest>

    <route id="error">
        <from uri="direct:error"/>
        <log message="Route(error): ${body}"/>

        <setHeader headerName="errMessageId">
            <jsonpath suppressExceptions="true">$[0].msgId</jsonpath>
        </setHeader>
        <setHeader headerName="errTicket">
            <jsonpath suppressExceptions="true">$[0].ticket</jsonpath>
        </setHeader>               
        <setHeader headerName="errHandled">
            <jsonpath suppressExceptions="true">$[0].handled</jsonpath>
        </setHeader>

        <log message="Route(error): Header name: errMessageId -> ${header[errMessageId]}"/>
        <log message="Route(error): Header name: errTicket -> ${header[errTicket]}"/>
        <log message="Route(error): Header name: errHandled -> ${header[errHandled]}"/>
    </route>
</camelContext>

访问相应节点的键时,我会在新设置的标头中获得值。

JSON 是这样发送的:所以你发送:

curl -XPOST http://localhost:9090/api/erroradmin -d '[{"var1": 10,"var2": 20}]' --header "Content-Type: application/json"

我正在使用的依赖项:

    <dependency>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-jackson</artifactId>
        <version>${camel.version}</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.8</version>
    </dependency>
    <dependency>
        <groupId>org.apache.camel</groupId>
        <artifactId>camel-jsonpath</artifactId>
        <version>${camel.version}</version>
    </dependency>
  </dependencies>

希望这对将来的人有所帮助!

编辑:确保使用骆驼 2.25.0 及更高版本。显然,当使用 camel-json 2.24.1 和相同的核心版本时,camel-json 将下载一个名为 json-smart 的过时依赖项,它会丢失一些让 JsonPath 正常工作的类。

于 2020-02-20T14:51:36.493 回答