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

Reply via email to