You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apache Camel:如何在不提前marshal的情况下条件设置Header、Body并使用toD()

Apache Camel:保留原Body同时实现动态HTTP路由配置

你的问题核心是修改Body后丢失了原MyCompositePojo实例,导致后续toD()无法访问configDetail.url。Camel的choice()完全支持条件设置Header,但修改Body会替换Exchange中的当前Body对象,这是问题的根源,和choice()本身无关。

下面是几种可行的解决方案,按实现复杂度从低到高排序:

方案1:暂存原Body到Exchange属性(最简洁)

在修改Body之前,把原MyCompositePojo存入Exchange的**属性(Property)**中(属性不需要序列化,适合路由内临时存储),后续需要访问原对象时直接从属性中获取:

from("direct:eventHttpChoice") 
    .log(....)
    .setProperty("originalComposite", body()) // 把原Body存入Exchange属性
    .setHeader("Authorization", simple("Bearer ${body.token}"))
    .setHeader(Exchange.HTTP_METHOD, simple("${body.httpMethod.name}"))
    .choice()
        .when(simple("${body.httpMethod.name} in 'PUT,DELETE'"))
            .setHeader(Exchange.HTTP_PATH, simple("${body.newEvent.number}"))
        .endChoice()
    .end()
    .choice()
        .when(simple("${body.httpMethod.name} in 'POST,PUT'"))
            .setHeader(HttpHeaders.CONTENT_TYPE, constant(MediaType.APPLICATION_JSON))
            .setBody(simple("${body.newEvent}")).marshal().json(JsonLibrary.Jsonb)
        .endChoice()
        .otherwise() // DELETE
            .when(simple("${body.configDetail.http.deleteSomeField} == 'true' && ${body.newEvent.someField} != null && ${body.newEvent.someField} != ''"))
                .setHeader(Exchange.HTTP_QUERY, simple("someField=${body.newEvent.someField}&operationId=${body.newEvent.operationId}"))
            .endChoice()
            .otherwise()
                .setHeader(Exchange.HTTP_QUERY, simple("operationId=${body.newEvent.operationId}"))
            .endChoice()
        .endChoice()
    .end()
    .log(LoggingLevel.INFO, "Sending to this url: ${exchangeProperty.originalComposite.configDetail.url}")
    .toD("${exchangeProperty.originalComposite.configDetail.url}", 10); // 从属性中获取原对象的URL

方案2:使用自定义Header暂存原Body

和方案1逻辑一致,只是把原对象存入Header而非Property。注意如果路由跨序列化场景(比如跨JVM、消息队列),Header需要支持序列化:

from("direct:eventHttpChoice") 
    .log(....)
    .setHeader("originalComposite", body()) // 把原Body存入Header
    // ... 中间逻辑不变
    .log(LoggingLevel.INFO, "Sending to this url: ${header.originalComposite.configDetail.url}")
    .toD("${header.originalComposite.configDetail.url}", 10);

方案3:用Bean封装HTTP配置逻辑(面向对象)

将HTTP头、请求体的配置逻辑封装到一个Java Bean中,全程不修改原Body,而是把需要的请求体存入Header,后续toD()通过Header指定请求体:

第一步:编写配置Bean

import jakarta.json.bind.Jsonb;
import jakarta.json.bind.JsonbBuilder;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.springframework.http.MediaType;

import java.nio.charset.StandardCharsets;

public class HttpRequestConfigurer {
    public void configure(Exchange exchange) {
        Message in = exchange.getIn();
        MyCompositePojo composite = in.getBody(MyCompositePojo.class);
        
        // 配置HTTP_PATH(PUT/DELETE场景)
        String method = composite.getHttpMethod().name();
        if ("PUT".equals(method) || "DELETE".equals(method)) {
            in.setHeader(Exchange.HTTP_PATH, composite.getNewEvent().getNumber());
        }
        
        // 配置请求体(POST/PUT场景)
        if ("POST".equals(method) || "PUT".equals(method)) {
            in.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
            Jsonb jsonb = JsonbBuilder.create();
            String payloadJson = jsonb.toJson(composite.getNewEvent());
            in.setHeader("requestPayload", payloadJson.getBytes(StandardCharsets.UTF_8));
        }
        
        // 配置查询参数(DELETE场景)
        if ("DELETE".equals(method)) {
            boolean deleteSomeField = "true".equals(composite.getConfigDetail().getHttp().getDeleteSomeField());
            String someField = composite.getNewEvent().getSomeField();
            String operationId = composite.getNewEvent().getOperationId();
            
            if (deleteSomeField && someField != null && !someField.isEmpty()) {
                in.setHeader(Exchange.HTTP_QUERY, String.format("someField=%s&operationId=%s", someField, operationId));
            } else {
                in.setHeader(Exchange.HTTP_QUERY, String.format("operationId=%s", operationId));
            }
        }
    }
}

第二步:在路由中调用Bean

from("direct:eventHttpChoice") 
    .log(....)
    .setHeader("Authorization", simple("Bearer ${body.token}"))
    .setHeader(Exchange.HTTP_METHOD, simple("${body.httpMethod.name}"))
    .bean(HttpRequestConfigurer.class) // 调用Bean完成配置,原Body不变
    .log(LoggingLevel.INFO, "Sending to this url: ${body.configDetail.url}")
    // 通过body参数指定从Header取请求体
    .toD("${body.configDetail.url}?body=${header.requestPayload}", 10);

关键注意点

  • Exchange的Body是单一值,修改后会覆盖原对象,这是Camel的核心设计,和choice()无关;
  • Exchange属性(exchangeProperty)是路由内的临时存储,不会被序列化,适合存储复杂对象;
  • 如果需要跨组件传递原对象,确保对象实现Serializable,再存入Header。

内容的提问来源于stack exchange,提问作者WesternGun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 01:10:27