Apache-camel:如何将json object(由curl发送)处理成headers?

Apache-camel: How to process a json object (sent by curl) into headers?

我有一个带有 apache camel 的 springboot 应用程序。我在其中有一个 camel-context。 我正在尝试通过带有密钥对值的 curl 发送 json 并通过路由对其进行处理。

发送数据:

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

Camel-context.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>

我得到以下 Stacktrace:

org.apache.camel.InvalidPayloadException: No body available of type: java.io.InputStream but has value: {msgId=D507B9EE-176D-4F3C-88E7-9E36CC2B9731, ticket=ERR001} of type: java.util.LinkedHashMap on: HttpMessage@0x28c1a31a. Caused by: No type converter available to convert from type: java.util.LinkedHashMap to the required type: java.io.InputStream with value {msgId=D507B9EE-176D-4F3C-88E7-9E36CC2B9731, ticket=ERR001}. Exchange[09395660-c947-47f1-b00f-d0d3030a39d1]. Caused by: [org.apache.camel.NoTypeConversionAvailableException - No type converter available to convert from type: java.util.LinkedHashMap to the required type: java.io.InputStream with value {msgId=D507B9EE-176D-4F3C-88E7-9E36CC2B9731, ticket=ERR001}]

欢迎使用 Whosebug! 我坚信第 13 行提到的 bindingMode="json" 是这次失败的根本原因。 manual 表示

When using binding you must also configure what POJO type to map to. This is mandatory for incoming messages, and optional for outgoing.

我真的很害怕 XML DSL,但是,这里是 Java.

中近似等效的其余 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项目是available here,如果你想玩


编辑:附加 header 处理步骤

        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'}");

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

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

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

<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>

当访问相应节点的键时,我得到了我新设置的值header。

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>

希望这对以后的人有所帮助!

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