This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 9e192fd3aefa CAMEL-25309: camel-http - a Map or List body says to
marshal it to JSON first (#27331)
9e192fd3aefa is described below
commit 9e192fd3aefa5ece0d9e322e9051e1d00e6ada23
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Oct 4 15:39:20 2026 +0200
CAMEL-25309: camel-http - a Map or List body says to marshal it to JSON
first (#27331)
* CAMEL-25309: camel-http - a Map or List body says to marshal it to JSON
first
A Map or a List (a body unmarshalled from JSON, or built as a map) cannot
become an HTTP request body, and the converter's message only named the
conversion: "No body available of type: java.io.InputStream but has type:
java.util.LinkedHashMap". The InvalidPayloadException keeps its type and now
adds: a Map is not an HTTP request body: marshal it to JSON first (marshal:
json) or set the body to the JSON text.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
* CAMEL-25309: name a Set or other Collection body as such in the hint
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01STT6whBgK1AqsSsUKrnE8m
---------
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
.../apache/camel/component/http/HttpProducer.java | 26 +++++++-
.../component/http/HttpProducerMapBodyTest.java | 78 ++++++++++++++++++++++
2 files changed, 103 insertions(+), 1 deletion(-)
diff --git
a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java
b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java
index 08b4267a2554..c689b99da43d 100644
---
a/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java
+++
b/components/camel-http/src/main/java/org/apache/camel/component/http/HttpProducer.java
@@ -27,6 +27,7 @@ import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -38,6 +39,7 @@ import java.util.Map.Entry;
import org.apache.camel.CamelExchangeException;
import org.apache.camel.Exchange;
import org.apache.camel.ExchangePropertyKey;
+import org.apache.camel.InvalidPayloadException;
import org.apache.camel.LineNumberAware;
import org.apache.camel.Message;
import org.apache.camel.RuntimeCamelException;
@@ -713,6 +715,28 @@ public class HttpProducer extends DefaultProducer
implements LineNumberAware {
return create;
}
+ /**
+ * The body as a stream for the request entity. A Map or a List (a body
unmarshalled from JSON, or built as a map)
+ * cannot become one, and the converter's message only names the
conversion: say what to do (CAMEL-25309).
+ */
+ private static InputStream mandatoryBodyStream(Exchange exchange, Message
in) throws InvalidPayloadException {
+ try {
+ return in.getMandatoryBody(InputStream.class);
+ } catch (InvalidPayloadException e) {
+ Object body = in.getBody();
+ if (body instanceof Map || body instanceof Collection) {
+ InvalidPayloadException answer = new InvalidPayloadException(
+ exchange, InputStream.class, in,
+ "a " + (body instanceof Map ? "Map" : body instanceof
List ? "List" : "Collection")
+ + " is not an HTTP
request body: marshal it to JSON first "
+ + "(marshal: json) or
set the body to the JSON text");
+ answer.initCause(e.getCause() != null ? e.getCause() : e);
+ throw answer;
+ }
+ throw e;
+ }
+ }
+
/**
* Creates a holder object for the data to send to the remote server.
*
@@ -833,7 +857,7 @@ public class HttpProducer extends DefaultProducer
implements LineNumberAware {
// fallback as input stream
if (answer == null) {
// force the body as an input stream since this is the
fallback
- InputStream is =
in.getMandatoryBody(InputStream.class);
+ InputStream is = mandatoryBodyStream(exchange, in);
if (multipart) {
answer =
MultipartEntityBuilder.create().addBinaryBody(multipartName, is).build();
} else {
diff --git
a/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerMapBodyTest.java
b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerMapBodyTest.java
new file mode 100644
index 000000000000..6b75ae3810f2
--- /dev/null
+++
b/components/camel-http/src/test/java/org/apache/camel/component/http/HttpProducerMapBodyTest.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.http;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.component.http.handler.BasicValidationHandler;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.junit.jupiter.api.Test;
+
+import static org.apache.camel.component.http.HttpMethods.POST;
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * A Map or List body cannot be an HTTP request body; the error says to
marshal it to JSON first (CAMEL-25309).
+ */
+public class HttpProducerMapBodyTest extends BaseHttpTest {
+
+ private HttpServer localServer;
+ private String endpointUrl;
+
+ @Override
+ public void setupResources() throws Exception {
+ localServer = ServerBootstrap.bootstrap()
+
.setCanonicalHostName("localhost").setHttpProcessor(getBasicHttpProcessor())
+
.setConnectionReuseStrategy(getConnectionReuseStrategy()).setResponseFactory(getHttpResponseFactory())
+ .register("/post", new BasicValidationHandler(POST.name(),
null, null, getExpectedContent()))
+ .create();
+ localServer.start();
+ endpointUrl = "http://localhost:" + localServer.getLocalPort();
+ }
+
+ @Override
+ public void cleanupResources() throws Exception {
+ if (localServer != null) {
+ localServer.stop();
+ }
+ }
+
+ @Test
+ public void aMapBodySaysToMarshalItToJson() {
+ Exchange exchange = template.request(endpointUrl +
"/post?httpMethod=POST",
+ // the body after unmarshal: json with Jackson
+ e -> e.getIn().setBody(new LinkedHashMap<>(Map.of("orderId",
"ORD-1001", "qty", 2))));
+
+
assertThat(exchange.getException()).isInstanceOf(InvalidPayloadException.class)
+
.hasMessageContaining("java.util.LinkedHashMap").hasMessageContaining("a Map is
not an HTTP request body")
+ .hasMessageContaining("marshal it to JSON first (marshal:
json)");
+ }
+
+ @Test
+ public void aListBodySaysToMarshalItToJson() {
+ Exchange exchange = template.request(endpointUrl +
"/post?httpMethod=POST",
+ e -> e.getIn().setBody(List.of(Map.of("sku", "CAMEL-MUG"))));
+
+
assertThat(exchange.getException()).isInstanceOf(InvalidPayloadException.class)
+ .hasMessageContaining("a List is not an HTTP request body");
+ }
+}