diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/http-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/http-component.adoc
index 094bfccca34a2..2b461b9b1921d 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/http-component.adoc
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/http-component.adoc
@@ -46,6 +46,116 @@ include::partial$component-endpoint-headers.adoc[]
== Usage
+=== Which HTTP method will be used
+
+IMPORTANT: Without the `httpMethod` option, the message body decides the method: a message with a body is sent as a
+`POST`, and a message without a body as a `GET`. After `unmarshal`, `split` or `setBody` the message has a body, so a
+call meant as a GET is sent as a POST. Set `httpMethod: GET` on the endpoint when you mean a GET; a GET sends no body
+(unless `getWithBody` is true).
+
+The method is chosen in this order:
+
+. The `httpMethod` option of the endpoint.
+. The `CamelHttpMethod` header.
+. `GET` when the `CamelHttpQuery` header, or the `CamelHttpUri` header, has a query string.
+. `GET` when the endpoint URI has a query string.
+. `POST` when the message has a body.
+. `GET` otherwise.
+
+For example, a stock lookup for each line of an order is a GET, whatever the line in the body is:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:checkStock")
+ .split(simple("${body[lines]}"))
+ .toD("http://localhost:8080/stock/${body[sku]}?httpMethod=GET")
+ .log("Stock answer: ${body}")
+ .end();
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+ ${body[lines]}
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:checkStock
+ steps:
+ - split:
+ expression:
+ simple:
+ expression: "${body[lines]}"
+ steps:
+ - toD:
+ uri: "http://localhost:8080/stock/${body[sku]}"
+ parameters:
+ httpMethod: GET
+ - log:
+ message: "Stock answer: ${body}"
+----
+====
+
+To send the order as JSON, marshal the body first, and say that it is a POST:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:createOrder")
+ .marshal().json()
+ .to("http://localhost:8080/orders?httpMethod=POST");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:createOrder
+ steps:
+ - marshal:
+ json: {}
+ - to:
+ uri: http://localhost:8080/orders
+ parameters:
+ httpMethod: POST
+----
+====
+
=== Message Body
Camel will store the HTTP response from the external server on the _OUT_ body.
@@ -131,16 +241,6 @@ a `HttpOperationFailedException` is thrown immediately (unless
* Response body as a `java.lang.String`, if server provided a body as
response
-=== Which HTTP method will be used
-
-The following algorithm is used to determine what HTTP method should be used: +
- 1. Use method provided as endpoint configuration (`httpMethod`). +
- 2. Use method provided in header (`Exchange.HTTP_METHOD`). +
- 3. `GET` if query string is provided in header. +
- 4. `GET` if endpoint is configured with a query string. +
- 5. `POST` if there is data to send (body is not `null`). +
- 6. `GET` otherwise.
-
=== Configuring URI to call
You can set the HTTP producer's URI directly from the endpoint URI. In
@@ -314,8 +414,9 @@ YAML::
=== How to set the http method (GET/PATCH/POST/PUT/DELETE/HEAD/OPTIONS/TRACE) to the HTTP producer
-The HTTP component provides a way to set the HTTP request method by
-setting the message header. Here is an example:
+Set the `httpMethod` option of the endpoint, as in <<_which_http_method_will_be_used,Which HTTP method will be used>>.
+The `CamelHttpMethod` header sets the method for each message, when the endpoint has no `httpMethod` option.
+Here is an example with the header:
[tabs]
====
diff --git a/components/camel-http/src/main/docs/http-component.adoc b/components/camel-http/src/main/docs/http-component.adoc
index 094bfccca34a2..2b461b9b1921d 100644
--- a/components/camel-http/src/main/docs/http-component.adoc
+++ b/components/camel-http/src/main/docs/http-component.adoc
@@ -46,6 +46,116 @@ include::partial$component-endpoint-headers.adoc[]
== Usage
+=== Which HTTP method will be used
+
+IMPORTANT: Without the `httpMethod` option, the message body decides the method: a message with a body is sent as a
+`POST`, and a message without a body as a `GET`. After `unmarshal`, `split` or `setBody` the message has a body, so a
+call meant as a GET is sent as a POST. Set `httpMethod: GET` on the endpoint when you mean a GET; a GET sends no body
+(unless `getWithBody` is true).
+
+The method is chosen in this order:
+
+. The `httpMethod` option of the endpoint.
+. The `CamelHttpMethod` header.
+. `GET` when the `CamelHttpQuery` header, or the `CamelHttpUri` header, has a query string.
+. `GET` when the endpoint URI has a query string.
+. `POST` when the message has a body.
+. `GET` otherwise.
+
+For example, a stock lookup for each line of an order is a GET, whatever the line in the body is:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:checkStock")
+ .split(simple("${body[lines]}"))
+ .toD("http://localhost:8080/stock/${body[sku]}?httpMethod=GET")
+ .log("Stock answer: ${body}")
+ .end();
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+ ${body[lines]}
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:checkStock
+ steps:
+ - split:
+ expression:
+ simple:
+ expression: "${body[lines]}"
+ steps:
+ - toD:
+ uri: "http://localhost:8080/stock/${body[sku]}"
+ parameters:
+ httpMethod: GET
+ - log:
+ message: "Stock answer: ${body}"
+----
+====
+
+To send the order as JSON, marshal the body first, and say that it is a POST:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:createOrder")
+ .marshal().json()
+ .to("http://localhost:8080/orders?httpMethod=POST");
+----
+
+XML::
++
+[source,xml]
+----
+
+
+
+
+
+
+
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+ from:
+ uri: direct:createOrder
+ steps:
+ - marshal:
+ json: {}
+ - to:
+ uri: http://localhost:8080/orders
+ parameters:
+ httpMethod: POST
+----
+====
+
=== Message Body
Camel will store the HTTP response from the external server on the _OUT_ body.
@@ -131,16 +241,6 @@ a `HttpOperationFailedException` is thrown immediately (unless
* Response body as a `java.lang.String`, if server provided a body as
response
-=== Which HTTP method will be used
-
-The following algorithm is used to determine what HTTP method should be used: +
- 1. Use method provided as endpoint configuration (`httpMethod`). +
- 2. Use method provided in header (`Exchange.HTTP_METHOD`). +
- 3. `GET` if query string is provided in header. +
- 4. `GET` if endpoint is configured with a query string. +
- 5. `POST` if there is data to send (body is not `null`). +
- 6. `GET` otherwise.
-
=== Configuring URI to call
You can set the HTTP producer's URI directly from the endpoint URI. In
@@ -314,8 +414,9 @@ YAML::
=== How to set the http method (GET/PATCH/POST/PUT/DELETE/HEAD/OPTIONS/TRACE) to the HTTP producer
-The HTTP component provides a way to set the HTTP request method by
-setting the message header. Here is an example:
+Set the `httpMethod` option of the endpoint, as in <<_which_http_method_will_be_used,Which HTTP method will be used>>.
+The `CamelHttpMethod` header sets the method for each message, when the endpoint has no `httpMethod` option.
+Here is an example with the header:
[tabs]
====
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 ea868f48218fc..d0a494bc72cb5 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
@@ -746,18 +746,27 @@ private boolean isCreateNewURL(Exchange exchange) {
* The body as a stream for the request entity. A Map, a List or an object (a body unmarshalled from JSON, XML or
* CSV, or built in the route) cannot become one, and the converter's message only names the conversion: say what
* the body is, the step that set it, and the marshal that writes it, in the data format the Content-Type names
- * (CAMEL-25309, CAMEL-25749).
+ * (CAMEL-25309, CAMEL-25749). When neither the httpMethod option nor the CamelHttpMethod header set the method, the
+ * body made the call a POST, which is often meant as a GET (a lookup for each line of a split order): say that too
+ * (CAMEL-25745).
*/
- private static InputStream mandatoryBodyStream(Exchange exchange, Message in) throws InvalidPayloadException {
+ private InputStream mandatoryBodyStream(Exchange exchange, Message in) throws InvalidPayloadException {
try {
return in.getMandatoryBody(InputStream.class);
} catch (InvalidPayloadException e) {
// what the body is, the step that set it, and the marshal that writes it (CAMEL-25309, CAMEL-25749)
String hint = BodyFacts.hint(exchange, BodyFacts.Need.BYTES, ExchangeHelper.getContentType(exchange));
if (hint != null) {
- InvalidPayloadException answer = new InvalidPayloadException(
- exchange, InputStream.class, in,
- BodyFacts.describe(exchange) + ", which is not an HTTP request body: " + hint);
+ String message = BodyFacts.describe(exchange) + ", which is not an HTTP request body";
+ if (getEndpoint().getHttpMethod() == null && in.getHeader(HttpConstants.HTTP_METHOD) == null) {
+ // the body made the call a POST (CAMEL-25745)
+ message += ". The call is a POST because the message has a body and neither the httpMethod option"
+ + " nor the CamelHttpMethod header sets the method: for a GET, set httpMethod GET on the"
+ + " endpoint (a GET sends no body); for a POST, " + hint;
+ } else {
+ message += ": " + hint;
+ }
+ InvalidPayloadException answer = new InvalidPayloadException(exchange, InputStream.class, in, message);
answer.initCause(e.getCause() != null ? e.getCause() : e);
throw answer;
}
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
index 7153bed9e106a..1e45694f9bae0 100644
--- 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
@@ -66,7 +66,22 @@ public void aMapBodySaysToMarshalIt() {
assertThat(exchange.getException()).isInstanceOf(InvalidPayloadException.class)
.hasMessageContaining("the body is a Map (LinkedHashMap), which is not an HTTP request body")
.hasMessageContaining("marshal it first with the data format to write (such as marshal: json,"
- + " marshal: jacksonXml or marshal: csv), or set the body to the text");
+ + " marshal: jacksonXml or marshal: csv), or set the body to the text")
+ // the method is set, so it is not about GET
+ .hasMessageNotContaining("set httpMethod GET");
+ }
+
+ @Test
+ public void aMapBodyWithoutAMethodSaysHowToSendAGet() {
+ // no httpMethod option and no CamelHttpMethod header: the body made the call a POST (CAMEL-25745)
+ Exchange exchange = template.request(endpointUrl + "/post",
+ e -> e.getIn().setBody(new LinkedHashMap<>(Map.of("sku", "CAMEL-MUG", "qty", 2))));
+
+ assertThat(exchange.getException()).isInstanceOf(InvalidPayloadException.class)
+ .hasMessageContaining("the body is a Map (LinkedHashMap), which is not an HTTP request body")
+ .hasMessageContaining("The call is a POST because the message has a body")
+ .hasMessageContaining("for a GET, set httpMethod GET on the endpoint")
+ .hasMessageContaining("for a POST, marshal it first with the data format to write");
}
@Test