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 1f62876382f6 CAMEL-24986: a REST producer says which path parameter
has no value (#26831)
1f62876382f6 is described below
commit 1f62876382f6508d1b5242313214826bac8e98f9
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Sep 24 10:53:48 2026 +0200
CAMEL-24986: a REST producer says which path parameter has no value (#26831)
A REST producer sent the request even when a path parameter had no value,
so the call went out with the placeholder still in it and the service answered
404 for a path holding a {name}, with nothing saying why.
resolvePlaceholders reads a header and falls back to an exchange variable,
and left the placeholder as it was when neither had a value. It now says which
parameter it is and where the value comes from:
The path parameter {sku} of /api/stock/{sku}/reserve has no value: set
the header sku, or an exchange variable of that name, before the call.
Only a name in the braces counts, so a uri that holds braces for another
reason is untouched. This changes behaviour: a request with an unresolved
placeholder cannot succeed, so failing at the producer is more useful than a
404 from the far end, and the test that pinned the old behaviour now asserts
the message.
Closes #26831
---
.../apache/camel/component/rest/RestProducer.java | 34 ++++++++++++++++++++++
.../component/rest/RestProducerAdvancedTest.java | 13 +++++----
2 files changed, 41 insertions(+), 6 deletions(-)
diff --git
a/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestProducer.java
b/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestProducer.java
index 8b8abd47c963..9eb40e0e4294 100644
---
a/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestProducer.java
+++
b/components/camel-rest/src/main/java/org/apache/camel/component/rest/RestProducer.java
@@ -29,6 +29,7 @@ import org.apache.camel.AsyncCallback;
import org.apache.camel.AsyncProcessor;
import org.apache.camel.AsyncProducer;
import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExchangeException;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.apache.camel.Producer;
@@ -167,6 +168,17 @@ public class RestProducer extends DefaultAsyncProducer {
}
}
resolvedUriTemplate = uriTemplateBuilder.toString();
+
+ // a placeholder with no value would be sent as it is, and the
service answers 404 for a path that
+ // holds a {name}: say which parameter it is instead
(CAMEL-24986)
+ String unresolved = firstPlaceholder(resolvedUriTemplate);
+ if (unresolved != null) {
+ throw new CamelExchangeException(
+ "The path parameter {" + unresolved + "} of " +
resolvedUriTemplate + " has no value:"
+ + " set the header " +
unresolved + ", or an exchange variable of"
+ + " that name, before the
call.",
+ exchange);
+ }
}
}
@@ -221,6 +233,28 @@ public class RestProducer extends DefaultAsyncProducer {
}
}
+ /**
+ * The name of the first {@code {name}} left in the template, or null when
every one of them was resolved
+ * (CAMEL-24986).
+ */
+ private static String firstPlaceholder(String uriTemplate) {
+ int start = uriTemplate.indexOf('{');
+ while (start >= 0) {
+ int end = uriTemplate.indexOf('}', start);
+ if (end < 0) {
+ return null;
+ }
+ String name = uriTemplate.substring(start + 1, end);
+ // a name, not something else that happens to be in braces
+ if (!name.isEmpty() && name.chars().allMatch(c ->
Character.isLetterOrDigit(c) || c == '_' || c == '-'
+ || c == '.')) {
+ return name;
+ }
+ start = uriTemplate.indexOf('{', end);
+ }
+ return null;
+ }
+
/**
* Replaces placeholders "{}" with message header or exchange variable
values.
*
diff --git
a/components/camel-rest/src/test/java/org/apache/camel/component/rest/RestProducerAdvancedTest.java
b/components/camel-rest/src/test/java/org/apache/camel/component/rest/RestProducerAdvancedTest.java
index 9680e1d5d00e..d288a2902cae 100644
---
a/components/camel-rest/src/test/java/org/apache/camel/component/rest/RestProducerAdvancedTest.java
+++
b/components/camel-rest/src/test/java/org/apache/camel/component/rest/RestProducerAdvancedTest.java
@@ -19,6 +19,7 @@ package org.apache.camel.component.rest;
import java.util.HashMap;
import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExchangeException;
import org.apache.camel.Exchange;
import org.apache.camel.Producer;
import org.apache.camel.impl.DefaultCamelContext;
@@ -32,6 +33,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
@ExtendWith(MockitoExtension.class)
class RestProducerAdvancedTest {
@@ -84,12 +86,11 @@ class RestProducerAdvancedTest {
RestProducer producer = new RestProducer(endpoint, mockProducer,
config);
Exchange exchange = new DefaultExchange(camelContext);
- // Don't set the header, so placeholder won't be resolved
- producer.prepareExchange(exchange);
-
- // When placeholder is not resolved, REST_HTTP_URI should not be set
- String uri =
exchange.getMessage().getHeader(RestConstants.REST_HTTP_URI, String.class);
- assertThat(uri).isNull();
+ // no header, so the placeholder has no value: the request would go
out with {userId} in the path and the
+ // service would answer 404 for it, so it fails here instead and says
which parameter it is (CAMEL-24986)
+ CamelExchangeException e = assertThrows(CamelExchangeException.class,
() -> producer.prepareExchange(exchange));
+ assertThat(e.getMessage()).contains("The path parameter
{userId}").contains("set the header userId");
+
assertThat(exchange.getMessage().getHeader(RestConstants.REST_HTTP_URI,
String.class)).isNull();
}
@Test