Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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]
----
<route>
<from uri="direct:checkStock"/>
<split>
<simple>${body[lines]}</simple>
<toD uri="http://localhost:8080/stock/${body[sku]}?httpMethod=GET"/>
<log message="Stock answer: ${body}"/>
</split>
</route>
----

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]
----
<route>
<from uri="direct:createOrder"/>
<marshal>
<json/>
</marshal>
<to uri="http://localhost:8080/orders?httpMethod=POST"/>
</route>
----

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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]
====
Expand Down
125 changes: 113 additions & 12 deletions components/camel-http/src/main/docs/http-component.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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]
----
<route>
<from uri="direct:checkStock"/>
<split>
<simple>${body[lines]}</simple>
<toD uri="http://localhost:8080/stock/${body[sku]}?httpMethod=GET"/>
<log message="Stock answer: ${body}"/>
</split>
</route>
----

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]
----
<route>
<from uri="direct:createOrder"/>
<marshal>
<json/>
</marshal>
<to uri="http://localhost:8080/orders?httpMethod=POST"/>
</route>
----

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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]
====
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading