This is an automated email from the ASF dual-hosted git repository.
jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/main by this push:
new bea0368ed0 Add support for specifying a content type for dynamic Qute
templates
bea0368ed0 is described below
commit bea0368ed09de82853e3c7d1ff0eb528e8038d7a
Author: James Netherton <[email protected]>
AuthorDate: Tue Nov 18 10:59:23 2025 +0000
Add support for specifying a content type for dynamic Qute templates
Fixes #7955
---
.../ROOT/pages/reference/extensions/qute.adoc | 10 +++
extensions/qute/component/pom.xml | 5 ++
.../org/apache/camel/component/qute/qute.json | 5 +-
.../apache/camel/component/qute/QuteConstants.java | 2 +
.../apache/camel/component/qute/QuteEndpoint.java | 87 +++++++++++++---------
.../camel/component/qute/QuteContentTypeTest.java | 62 +++++++++++++++
.../{log4j2.properties => logging.properties} | 27 ++++---
.../org/apache/camel/component/qute/hello.html | 28 +++++++
extensions/qute/runtime/src/main/doc/usage.adoc | 10 +++
.../quarkus/component/qute/it/QuteResource.java | 18 +++++
.../camel/quarkus/component/qute/it/QuteRoute.java | 5 +-
.../qute/src/main/resources/application.properties | 2 +-
.../qute/src/main/resources/templates/hello.html | 23 ++++++
.../camel/quarkus/component/qute/it/QuteTest.java | 27 ++++++-
14 files changed, 258 insertions(+), 53 deletions(-)
diff --git a/docs/modules/ROOT/pages/reference/extensions/qute.adoc
b/docs/modules/ROOT/pages/reference/extensions/qute.adoc
index abdc38cb62..c57a293c95 100644
--- a/docs/modules/ROOT/pages/reference/extensions/qute.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/qute.adoc
@@ -125,6 +125,16 @@ from("direct:start")
.to("qute:dynamic?allowTemplateFromHeader=true");
---------------------------------------------------------------------------------------------
+Dynamic template content with specified content type.
+
+[source,java]
+---------------------------------------------------------------------------------------------
+from("direct:start")
+
.setHeader(QuteConstants.QUTE_TEMPLATE).constant("<hello>{headers.greeting}</hello>")
+ .setHeader(QuteConstants.QUTE_TEMPLATE_CONTENT_TYPE).constant("text/html")
+ .to("qute:dynamic?allowTemplateFromHeader=true");
+---------------------------------------------------------------------------------------------
+
Dynamic template instance.
[source,java]
diff --git a/extensions/qute/component/pom.xml
b/extensions/qute/component/pom.xml
index 8d812764fa..0b3c035df8 100644
--- a/extensions/qute/component/pom.xml
+++ b/extensions/qute/component/pom.xml
@@ -85,6 +85,11 @@
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git
a/extensions/qute/component/src/generated/resources/META-INF/org/apache/camel/component/qute/qute.json
b/extensions/qute/component/src/generated/resources/META-INF/org/apache/camel/component/qute/qute.json
index 07e0228d0e..24be95e7dd 100644
---
a/extensions/qute/component/src/generated/resources/META-INF/org/apache/camel/component/qute/qute.json
+++
b/extensions/qute/component/src/generated/resources/META-INF/org/apache/camel/component/qute/qute.json
@@ -33,12 +33,13 @@
"CamelQuteResourceUri": { "index": 0, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "A URI for the template resource to use instead of the
endpoint configured one.", "constantName":
"org.apache.camel.component.qute.QuteConstants#QUTE_RESOURCE_URI" },
"CamelQuteTemplate": { "index": 1, "kind": "header", "displayName": "",
"group": "producer", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The template to use instead of the endpoint configured
one.", "constantName":
"org.apache.camel.component.qute.QuteConstants#QUTE_TEMPLATE" },
"CamelQuteTemplateInstance": { "index": 2, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "TemplateInstance", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The template instance to
use instead of the endpoint configured one.", "constantName":
"org.apache.camel.component.qute.QuteConstants#QUTE_TEMPLATE_INSTANCE" },
- "CamelQuteTemplateData": { "index": 3, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType": "Map",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The template model data.", "constantName":
"org.apache.camel.component.qute.QuteConstants#QUTE_TEMPLATE_DATA" }
+ "CamelQuteTemplateData": { "index": 3, "kind": "header", "displayName":
"", "group": "producer", "label": "", "required": false, "javaType": "Map",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The template model data.", "constantName":
"org.apache.camel.component.qute.QuteConstants#QUTE_TEMPLATE_DATA" },
+ "CamelQuteTemplateContentType": { "index": 4, "kind": "header",
"displayName": "", "group": "producer", "label": "", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The content type to use for templates
rendered from content provided from the CamelQuteTemplate header",
"constantName":
"org.apache.camel.component.qute.QuteConstants#QUTE_TEMPLATE_CONTENT_TYPE" }
},
"properties": {
"resourceUri": { "index": 0, "kind": "path", "displayName": "Resource
Uri", "group": "producer", "label": "", "required": true, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "supportFileReference": true,
"description": "Path to the resource. You can prefix with: classpath, file,
http, ref, or bean. classpath, file and http loads the resource using these
protocols (classpath is default). ref will look [...]
"allowContextMapAll": { "index": 1, "kind": "parameter", "displayName":
"Allow Context Map All", "group": "producer", "label": "", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Sets whether the
context map should allow access to all details. By default only the message
body and headers can be accessed. This option can be enabled for full access to
the current Exchange and C [...]
- "allowTemplateFromHeader": { "index": 2, "kind": "parameter",
"displayName": "Allow Template From Header", "group": "producer", "label": "",
"required": false, "type": "boolean", "javaType": "boolean", "deprecated":
false, "autowired": false, "secret": false, "defaultValue": false,
"description": "Whether to allow to use resource template from header or not
(default false). Enabling this allows to specify dynamic templates via message
header. However this can be seen as a potential s [...]
+ "allowTemplateFromHeader": { "index": 2, "kind": "parameter",
"displayName": "Allow Template From Header", "group": "producer", "label": "",
"required": false, "type": "boolean", "javaType": "boolean", "deprecated":
false, "autowired": false, "secret": false, "defaultValue": false,
"description": "Whether to allow to use resource template from header or not
(default false). Enabling this allows to specify dynamic templates via message
header. However, this can be seen as a potential [...]
"contentCache": { "index": 3, "kind": "parameter", "displayName": "Content
Cache", "group": "producer", "label": "", "required": false, "type": "boolean",
"javaType": "boolean", "deprecated": false, "autowired": false, "secret":
false, "defaultValue": true, "description": "Sets whether to use resource
content cache or not" },
"encoding": { "index": 4, "kind": "parameter", "displayName": "Encoding",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "Character encoding of the resource content." },
"lazyStartProducer": { "index": 5, "kind": "parameter", "displayName":
"Lazy Start Producer", "group": "producer (advanced)", "label":
"producer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether the producer should be started
lazy (on the first message). By starting lazy you can use this to allow
CamelContext and routes to startup in situations where a produc [...]
diff --git
a/extensions/qute/component/src/main/java/org/apache/camel/component/qute/QuteConstants.java
b/extensions/qute/component/src/main/java/org/apache/camel/component/qute/QuteConstants.java
index 386f13c6d7..e6af131437 100644
---
a/extensions/qute/component/src/main/java/org/apache/camel/component/qute/QuteConstants.java
+++
b/extensions/qute/component/src/main/java/org/apache/camel/component/qute/QuteConstants.java
@@ -27,6 +27,8 @@ public final class QuteConstants {
public static final String QUTE_TEMPLATE_INSTANCE =
"CamelQuteTemplateInstance";
@Metadata(description = "The template model data.", javaType = "Map")
public static final String QUTE_TEMPLATE_DATA = "CamelQuteTemplateData";
+ @Metadata(description = "The content type to use for templates rendered
from content provided from the CamelQuteTemplate header", javaType = "String")
+ public static final String QUTE_TEMPLATE_CONTENT_TYPE =
"CamelQuteTemplateContentType";
private QuteConstants() {
// Utility class
diff --git
a/extensions/qute/component/src/main/java/org/apache/camel/component/qute/QuteEndpoint.java
b/extensions/qute/component/src/main/java/org/apache/camel/component/qute/QuteEndpoint.java
index a166cc05dc..7baf8ecb08 100644
---
a/extensions/qute/component/src/main/java/org/apache/camel/component/qute/QuteEndpoint.java
+++
b/extensions/qute/component/src/main/java/org/apache/camel/component/qute/QuteEndpoint.java
@@ -20,11 +20,14 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
+import java.net.URLConnection;
import java.util.Map;
import java.util.Optional;
import io.quarkus.qute.Engine;
-import io.quarkus.qute.EngineBuilder;
+import io.quarkus.qute.HtmlEscaper;
+import io.quarkus.qute.ImmutableList;
+import io.quarkus.qute.Qute;
import io.quarkus.qute.ReflectionValueResolver;
import io.quarkus.qute.Template;
import io.quarkus.qute.TemplateException;
@@ -69,29 +72,17 @@ public class QuteEndpoint extends ResourceEndpoint {
private synchronized Engine getQuteEngine() {
if (quteEngine == null) {
- EngineBuilder builder = Engine.builder().addDefaults();
- builder.addValueResolver(ReflectionValueResolver::new);
- builder.addLocator(this::locate);
-
- quteEngine = builder.build();
+ quteEngine = Engine.builder()
+ .addDefaults()
+ .addValueResolver(ReflectionValueResolver::new)
+ .addLocator(this::locate)
+ .addParserHook(new Qute.IndexedArgumentsParserHook())
+ .addResultMapper(new
HtmlEscaper(ImmutableList.of("text/html", "text/xml")))
+ .build();
}
return quteEngine;
}
- public boolean isAllowTemplateFromHeader() {
- return allowTemplateFromHeader;
- }
-
- /**
- * Whether to allow to use resource template from header or not (default
false).
- *
- * Enabling this allows to specify dynamic templates via message header.
However this can
- * be seen as a potential security vulnerability if the header is coming
from a malicious user, so use this with care.
- */
- public void setAllowTemplateFromHeader(boolean allowTemplateFromHeader) {
- this.allowTemplateFromHeader = allowTemplateFromHeader;
- }
-
private Optional<TemplateLocation> locate(String path) {
return Optional.of(new TemplateLocation() {
private URL locatePath(String path) {
@@ -113,33 +104,24 @@ public class QuteEndpoint extends ResourceEndpoint {
in = locatePath(path).openStream();
}
- Reader reader = getEncoding() != null ? new
InputStreamReader(in, getEncoding())
- : new InputStreamReader(in);
- return reader;
+ return getEncoding() != null ? new InputStreamReader(in,
getEncoding()) : new InputStreamReader(in);
} catch (Exception e) {
- log.warn("Can not load template " + path + " due to " + e);
+ log.warn("Cannot load template {}", path, e);
return null;
}
}
@Override
public Optional<Variant> getVariant() {
+ String contentType =
URLConnection.getFileNameMap().getContentTypeFor(path);
+ if (ObjectHelper.isNotEmpty(contentType)) {
+ return Optional.of(Variant.forContentType(contentType));
+ }
return Optional.empty();
}
});
}
- /**
- * Character encoding of the resource content.
- */
- public void setEncoding(String encoding) {
- this.encoding = encoding;
- }
-
- public String getEncoding() {
- return encoding;
- }
-
public QuteEndpoint findOrCreateEndpoint(String uri, String
newResourceUri) {
String newUri = uri.replace(getResourceUri(), newResourceUri);
log.debug("Getting endpoint with URI: {}", newUri);
@@ -194,8 +176,14 @@ public class QuteEndpoint extends ResourceEndpoint {
throw new TemplateException("Unable to parse Qute template
from path: " + path);
}
} else {
- // This is the first time to parse the content
- template = engine.parse(content);
+ Variant variant = null;
+ String templateContentType =
exchange.getMessage().getHeader(QuteConstants.QUTE_TEMPLATE_CONTENT_TYPE,
+ String.class);
+ if (ObjectHelper.isNotEmpty(templateContentType)) {
+ variant = Variant.forContentType(templateContentType);
+ }
+
+ template = engine.parse(content, variant);
if (template == null) {
throw new TemplateException("Unable to parse Qute
template");
}
@@ -212,4 +200,29 @@ public class QuteEndpoint extends ResourceEndpoint {
exchange.getMessage().setBody(instance.render().trim());
}
+
+ /**
+ * Character encoding of the resource content.
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public boolean isAllowTemplateFromHeader() {
+ return allowTemplateFromHeader;
+ }
+
+ /**
+ * Whether to allow to use resource template from header or not (default
false).
+ *
+ * Enabling this allows to specify dynamic templates via message header.
However, this can
+ * be seen as a potential security vulnerability if the header is coming
from a malicious user, so use this with care.
+ */
+ public void setAllowTemplateFromHeader(boolean allowTemplateFromHeader) {
+ this.allowTemplateFromHeader = allowTemplateFromHeader;
+ }
}
diff --git
a/extensions/qute/component/src/test/java/org/apache/camel/component/qute/QuteContentTypeTest.java
b/extensions/qute/component/src/test/java/org/apache/camel/component/qute/QuteContentTypeTest.java
new file mode 100644
index 0000000000..688ff23e59
--- /dev/null
+++
b/extensions/qute/component/src/test/java/org/apache/camel/component/qute/QuteContentTypeTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.qute;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.camel.RoutesBuilder;
+import org.apache.camel.builder.RouteBuilder;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class QuteContentTypeTest extends QuteTestBase {
+ private static final String MESSAGE = "<h1>Hello World!</h1>";
+ private static final String MESSAGE_ENCODED = "<h1>Hello
World!</h1>";
+
+ @Test
+ void customTemplateContentType() throws IOException {
+ Map<String, Object> headers = new HashMap<>();
+
+ try (InputStream stream =
getClass().getResourceAsStream("hello.html")) {
+ if (stream == null) {
+ throw new IllegalArgumentException("hello.html not found");
+ }
+
+ String content =
context.getTypeConverter().convertTo(String.class, stream);
+
+ headers.put(QuteConstants.QUTE_TEMPLATE, content);
+ headers.put(QuteConstants.QUTE_TEMPLATE_CONTENT_TYPE, "text/html");
+
+ String result = template.requestBodyAndHeaders("direct:start",
MESSAGE, headers, String.class);
+ assertTrue(result.contains(MESSAGE_ENCODED));
+ }
+ }
+
+ @Override
+ protected RoutesBuilder createRouteBuilder() throws Exception {
+ return new RouteBuilder() {
+ public void configure() {
+ from("direct:start")
+ .to("qute:dynamic?allowTemplateFromHeader=true");
+ }
+ };
+ }
+}
diff --git a/extensions/qute/component/src/test/resources/log4j2.properties
b/extensions/qute/component/src/test/resources/logging.properties
similarity index 64%
rename from extensions/qute/component/src/test/resources/log4j2.properties
rename to extensions/qute/component/src/test/resources/logging.properties
index 60370f6c16..5813bac621 100644
--- a/extensions/qute/component/src/test/resources/log4j2.properties
+++ b/extensions/qute/component/src/test/resources/logging.properties
@@ -14,15 +14,20 @@
## See the License for the specific language governing permissions and
## limitations under the License.
## ---------------------------------------------------------------------------
+loggers=org.jboss.logmanager
-appender.file.type = File
-appender.file.name = file
-appender.file.fileName = target/camel-qute-test.log
-appender.file.layout.type = PatternLayout
-appender.file.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
-appender.out.type = Console
-appender.out.name = out
-appender.out.layout.type = PatternLayout
-appender.out.layout.pattern = %d [%-15.15t] %-5p %-30.30c{1} - %m%n
-rootLogger.level = INFO
-rootLogger.appenderRef.file.ref = file
+logger.level=INFO
+logger.handlers=CONSOLE
+
+logger.org.jboss.logmanager.useParentHandlers=true
+logger.org.jboss.logmanager.level=INFO
+
+handler.CONSOLE=org.jboss.logmanager.handlers.ConsoleHandler
+handler.CONSOLE.formatter=PATTERN
+handler.CONSOLE.properties=autoFlush,target
+handler.CONSOLE.autoFlush=true
+handler.CONSOLE.target=SYSTEM_OUT
+
+formatter.PATTERN=org.jboss.logmanager.formatters.PatternFormatter
+formatter.PATTERN.properties=pattern
+formatter.PATTERN.pattern=%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n
diff --git
a/extensions/qute/component/src/test/resources/org/apache/camel/component/qute/hello.html
b/extensions/qute/component/src/test/resources/org/apache/camel/component/qute/hello.html
new file mode 100644
index 0000000000..50b4a27a83
--- /dev/null
+++
b/extensions/qute/component/src/test/resources/org/apache/camel/component/qute/hello.html
@@ -0,0 +1,28 @@
+<!--
+
+ 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.
+
+-->
+<html>
+ <head>
+ <title>Greetings from Camel Quarkus Qute</title>
+ </head>
+ <body>
+ <code>
+ {body}
+ </code>
+ </body>
+</html>
diff --git a/extensions/qute/runtime/src/main/doc/usage.adoc
b/extensions/qute/runtime/src/main/doc/usage.adoc
index 728c03ff46..fbaa82afe1 100644
--- a/extensions/qute/runtime/src/main/doc/usage.adoc
+++ b/extensions/qute/runtime/src/main/doc/usage.adoc
@@ -80,6 +80,16 @@ from("direct:start")
.to("qute:dynamic?allowTemplateFromHeader=true");
---------------------------------------------------------------------------------------------
+Dynamic template content with specified content type.
+
+[source,java]
+---------------------------------------------------------------------------------------------
+from("direct:start")
+
.setHeader(QuteConstants.QUTE_TEMPLATE).constant("<hello>{headers.greeting}</hello>")
+ .setHeader(QuteConstants.QUTE_TEMPLATE_CONTENT_TYPE).constant("text/html")
+ .to("qute:dynamic?allowTemplateFromHeader=true");
+---------------------------------------------------------------------------------------------
+
Dynamic template instance.
[source,java]
diff --git
a/integration-tests/qute/src/main/java/org/apache/camel/quarkus/component/qute/it/QuteResource.java
b/integration-tests/qute/src/main/java/org/apache/camel/quarkus/component/qute/it/QuteResource.java
index 734a5396bc..a5172c4a8a 100644
---
a/integration-tests/qute/src/main/java/org/apache/camel/quarkus/component/qute/it/QuteResource.java
+++
b/integration-tests/qute/src/main/java/org/apache/camel/quarkus/component/qute/it/QuteResource.java
@@ -16,6 +16,9 @@
*/
package org.apache.camel.quarkus.component.qute.it;
+import java.util.HashMap;
+import java.util.Map;
+
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
@@ -23,6 +26,7 @@ import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import org.apache.camel.CamelExecutionException;
import org.apache.camel.ProducerTemplate;
@@ -61,4 +65,18 @@ public class QuteResource {
return e.getCause().getMessage();
}
}
+
+ @Path("/template/dynamic")
+ @POST
+ @Produces(MediaType.TEXT_PLAIN)
+ public String getDynamicTemplate(
+ @QueryParam("contentType") String contentType,
+ String templateContent) {
+
+ Map<String, Object> headers = new HashMap<>();
+ headers.put(QuteConstants.QUTE_TEMPLATE, templateContent);
+ headers.put(QuteConstants.QUTE_TEMPLATE_CONTENT_TYPE, contentType);
+
+ return producerTemplate.requestBodyAndHeaders("direct:dynamic",
"<h1>Hello World!</h1>", headers, String.class);
+ }
}
diff --git
a/integration-tests/qute/src/main/java/org/apache/camel/quarkus/component/qute/it/QuteRoute.java
b/integration-tests/qute/src/main/java/org/apache/camel/quarkus/component/qute/it/QuteRoute.java
index 3b982a6b48..7ecdb56fc6 100644
---
a/integration-tests/qute/src/main/java/org/apache/camel/quarkus/component/qute/it/QuteRoute.java
+++
b/integration-tests/qute/src/main/java/org/apache/camel/quarkus/component/qute/it/QuteRoute.java
@@ -22,6 +22,9 @@ public class QuteRoute extends RouteBuilder {
@Override
public void configure() throws Exception {
from("direct:test")
- .to("qute:hello");
+ .to("qute:hello.txt");
+
+ from("direct:dynamic")
+ .to("qute:dynamic?allowTemplateFromHeader=true");
}
}
diff --git a/integration-tests/qute/src/main/resources/application.properties
b/integration-tests/qute/src/main/resources/application.properties
index 1e3c63fa2b..d4a0032c9f 100644
--- a/integration-tests/qute/src/main/resources/application.properties
+++ b/integration-tests/qute/src/main/resources/application.properties
@@ -14,4 +14,4 @@
## See the License for the specific language governing permissions and
## limitations under the License.
## ---------------------------------------------------------------------------
-quarkus.qute.suffixes=htm,html,txt
+quarkus.qute.suffixes=html,txt
diff --git a/integration-tests/qute/src/main/resources/templates/hello.html
b/integration-tests/qute/src/main/resources/templates/hello.html
new file mode 100644
index 0000000000..f7b4b10450
--- /dev/null
+++ b/integration-tests/qute/src/main/resources/templates/hello.html
@@ -0,0 +1,23 @@
+<!--
+
+ 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.
+
+-->
+<html>
+ <body>
+ {body}
+ </body>
+</html>
diff --git
a/integration-tests/qute/src/test/java/org/apache/camel/quarkus/component/qute/it/QuteTest.java
b/integration-tests/qute/src/test/java/org/apache/camel/quarkus/component/qute/it/QuteTest.java
index 1f64f4fe19..6fd1051549 100644
---
a/integration-tests/qute/src/test/java/org/apache/camel/quarkus/component/qute/it/QuteTest.java
+++
b/integration-tests/qute/src/test/java/org/apache/camel/quarkus/component/qute/it/QuteTest.java
@@ -40,11 +40,36 @@ class QuteTest {
}
@Test
- public void tesTemplateContentFromHeader() {
+ public void testTemplateContentFromHeader() {
RestAssured.given()
.body("Hello {body}")
.post("/qute/template")
.then()
.body(is("Hello World"));
}
+
+ @Test
+ public void testDynamicTemplateWithContentType() {
+ String template = """
+ <html>
+ <body>{body}</body>
+ </html>
+ """;
+
+ // text/html will HTML encode the template data
+ RestAssured.given()
+ .queryParam("contentType", "text/html")
+ .body(template)
+ .post("/qute/template/dynamic")
+ .then()
+ .body(is(template.trim().replace("{body}", "<h1>Hello
World!</h1>")));
+
+ // text/plain will return the HTML template data as-is
+ RestAssured.given()
+ .queryParam("contentType", "text/plain")
+ .body(template)
+ .post("/qute/template/dynamic")
+ .then()
+ .body(is(template.trim().replace("{body}", "<h1>Hello
World!</h1>")));
+ }
}