The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.
Edit this Page

Motor de plantillas Qute

Qute es un motor de plantillas desarrollado específicamente para Quarkus. Se minimiza el uso de la reflexión para reducir el tamaño de las imágenes nativas. La API combina tanto el estilo de codificación imperativo como el reactivo no bloqueante. En el modo de desarrollo, todos los archivos ubicados en src/main/resources/templates se supervisan en busca de cambios, y las modificaciones se hacen visibles inmediatamente. Además, nuestro objetivo es detectar la mayoría de los problemas de plantilla en el momento de la compilación. En esta guía, aprenderá a renderizar fácilmente plantillas en su aplicación.

Solución

Recomendamos que siga las instrucciones de las siguientes secciones y cree la aplicación paso a paso. Sin embargo, también puede ir directamente al ejemplo completo.

Clone el repositorio Git: git clone https://github.com/quarkusio/quarkus-quickstarts.git o descargue un archivo.

La solución se encuentra en el qute-quickstart directorio.

Servir plantillas Qute a través de HTTP

Si desea servir sus plantillas a través de HTTP:

  1. La extensión Web de Qute le permite servir directamente a través de HTTP plantillas ubicadas en src/main/resources/templates/pub/. En ese caso no necesita ningún código Java para "conectar" la plantilla, por ejemplo, la plantilla src/main/resources/templates/pub/foo.html se servirá desde las rutas /foo y /foo.html por defecto.

  2. Para un control más fino, puede combinarlo con Quarkus REST para controlar cómo se servirá su plantilla. Todos los archivos ubicados en el directorio src/main/resources/templates y sus subdirectorios se registran como plantillas y pueden inyectarse en un recurso REST.

pom.xml
<dependency>
    <groupId>io.quarkiverse.qute.web</groupId>
    <artifactId>quarkus-qute-web</artifactId>
</dependency>
build.gradle
implementation("io.quarkiverse.qute.web:quarkus-qute-web")
La extensión Qute Web, aunque está alojada en Quarkiverse, forma parte de Quarkus Platform y su versión está definida en la lista de materiales de Quarkus Platform.

Sirviendo Hola Mundo con Qute

Empecemos con una plantilla Hola Mundo:

src/main/resources/templates/pub/hello.html
<h1>Hello {http:param('name', 'Quarkus')}!</h1> (1)
1 {http:param('name', 'Quarkus')} es una expresión que se evalúa cuando se renderiza la plantilla (Quarkus es el valor por defecto).
Las plantillas ubicadas en el directorio pub se sirven a través de HTTP. Este comportamiento está incorporado, no se necesitan controladores. Por ejemplo, la plantilla src/main/resources/templates/pub/foo.html se servirá desde las rutas /foo y /foo.html por defecto.

Una vez que su aplicación se esté ejecutando, puede abrir su navegador y navegar a: http://localhost:8080/hello?name=Martin

Para más información sobre las opciones de Qute Web, consulte la guía de Qute Web.

Hola Qute y REST

Para un control más preciso, puede combinar Qute Web con Quarkus REST (anteriormente RESTEasy Reactive) o la extensión heredada basada en RESTEasy Classic para controlar cómo se servirá su plantilla.

Utilizando la extensión quarkus-rest-qute:

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-rest-qute</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-rest-qute")

Una plantilla de texto muy sencilla:

hello.txt
Hello {name}! (1)
1 {name} es una expresión de valor que se evalúa cuando se renderiza la plantilla.

Ahora vamos a inyectar la plantilla "compilada" en la clase de recursos.

HelloResource.java
package org.acme.quarkus.sample;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.Template;

@Path("hello")
public class HelloResource {

    @Inject
    Template hello; (1)

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public TemplateInstance get(@QueryParam("name") String name) {
        return hello.data("name", name); (2) (3)
    }
}
1 Si no se proporciona el calificador @Location, se utiliza el nombre del campo para localizar la plantilla. En este caso particular, estamos inyectando una plantilla con la ruta templates/hello.txt.
2 Template.data() devuelve una nueva instancia de plantilla que se puede personalizar antes de que se active el renderizado real. En este caso, ponemos el valor del nombre bajo la clave name. El mapa de datos es accesible durante el renderizado.
3 Tenga en cuenta que nosotros no activamos la renderización - esto lo hace automáticamente una implementación especial de ContainerResponseFilter proporcionada por quarkus-rest-qute.

Si su aplicación se está ejecutando, puede solicitar el punto final:

$ curl -w "\n" http://localhost:8080/hello?name=Martin
Hello Martin!

Plantillas con seguridad de tipos

Hay una forma alternativa de declarar sus plantillas en su código Java, que se basa en la siguiente convención:

  • Organice sus archivos de plantillas en el directorio /src/main/resources/templates, agrupándolos en un directorio por cada clase de recurso. Así, si su clase FruitResource hace referencia a dos plantillas apples y oranges, colóquelas en /src/main/resources/templates/FruitResource/apples.txt y /src/main/resources/templates/FruitResource/oranges.txt. Agrupar las plantillas por clase de recurso facilita la navegación hasta ellas.

  • En cada una de sus clases de recursos, declare una clase @CheckedTemplate static class Template {} dentro de su clase de recursos.

  • Declare un public static native TemplateInstance method(); por archivo de plantilla para su recurso.

  • Utilice esos métodos estáticos para construir sus instancias de plantilla.

Este es el ejemplo anterior, reescrito con este estilo:

Empezaremos con una plantilla muy sencilla:

HelloResource/hello.txt
Hello {name}! (1)
1 {name} es una expresión de valor que se evalúa cuando se renderiza la plantilla.

Ahora vamos a declarar y utilizar esta plantilla en la clase de recursos.

HelloResource.java
package org.acme.quarkus.sample;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.CheckedTemplate;

@Path("hello")
public class HelloResource {

    @CheckedTemplate
    public static class Templates {
        public static native TemplateInstance hello(String name); (1)
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public TemplateInstance get(@QueryParam("name") String name) {
        return Templates.hello(name); (2)
    }
}
1 Esto declara una plantilla con la ruta templates/HelloResource/hello.
2 Templates.hello() devuelve una nueva instancia de plantilla que se devuelve desde el método del recurso. Tenga en cuenta que no activamos la renderización - esto lo hace automáticamente una implementación especial de ContainerResponseFilter proporcionada por quarkus-rest-qute.
Una vez que haya declarado una clase @CheckedTemplate, comprobaremos que todos sus métodos apuntan a plantillas existentes, así que si intenta utilizar una plantilla de su código Java y se olvidó de añadirla, se lo haremos saber en el momento de la compilación :)

Tenga en cuenta que este estilo de declaración le permite referenciar plantillas declaradas en otros recursos también:

GreetingResource.java
package org.acme.quarkus.sample;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import io.quarkus.qute.TemplateInstance;

@Path("greeting")
public class GreetingResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public TemplateInstance get(@QueryParam("name") String name) {
        return HelloResource.Templates.hello(name);
    }
}

Plantillas de nivel superior con seguridad de tipos

Naturalmente, si desea declarar plantillas en el nivel superior, directamente en /src/main/resources/templates/hello.txt, por ejemplo, puede declararlas en una clase de nivel superior (no anidada) Templates:

HelloResource.java
package org.acme.quarkus.sample;

import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.Template;
import io.quarkus.qute.CheckedTemplate;

@CheckedTemplate
public class Templates {
    public static native TemplateInstance hello(String name); (1)
}
1 Esto declara una plantilla con la ruta templates/hello.

Declaraciones de parámetros de plantillas

Si declara una declaración de parámetro en una plantilla, Qute intenta validar todas las expresiones que hacen referencia a este parámetro y si se encuentra una expresión incorrecta la compilación falla.

Supongamos que tenemos una clase simple como esta:

Item.java
public class Item {
    public String name;
    public BigDecimal price;
}

Y nos gustaría renderizar una página HTML simple que contenga el nombre del artículo y el precio.

Empecemos de nuevo con la plantilla:

ItemResource/item.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{item.name}</title> (1)
</head>
<body>
    <h1>{item.name}</h1>
    <div>Price: {item.price}</div> (2)
</body>
</html>
1 Esta expresión está validada. Intente cambiar la expresión a {item.nonSense} y la compilación debería fallar.
2 Esto también se valida.

Por último, vamos a crear una clase de recursos con plantillas con seguridad de tipos:

ItemResource.java
package org.acme.quarkus.sample;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.Template;
import io.quarkus.qute.CheckedTemplate;

@Path("item")
public class ItemResource {

    @CheckedTemplate
    public static class Templates {
        public static native TemplateInstance item(Item item); (1)
    }

    @GET
    @Path("{id}")
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance get(@PathParam("id") Integer id) {
        return Templates.item(service.findItem(id)); (2)
    }
}
1 Declare un método que nos da un TemplateInstance para templates/ItemResource/item.html y declare su parámetro Item item para que podamos validar la plantilla.
2 Haga que el objeto Item sea accesible en la plantilla.
Cuando el argumento del compilador --parameters está habilitado, Quarkus REST puede inferir los nombres de los parámetros a partir de los nombres de los argumentos del método, haciendo que la anotación @PathParam("id") sea opcional en este caso.

Declaración de parámetros de la plantilla dentro de la propia plantilla

Alternativamente, puede declarar los parámetros de la plantilla en el propio archivo de la plantilla.

Empecemos de nuevo con la plantilla:

item.html
{@org.acme.Item item} (1)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{item.name}</title> (2)
</head>
<body>
    <h1>{item.name}</h1>
    <div>Price: {item.price}</div>
</body>
</html>
1 Declaración opcional del parámetro. Qute intenta validar todas las expresiones que hacen referencia al parámetro item.
2 Esta expresión está validada. Intente cambiar la expresión a {item.nonSense} y la compilación debería fallar.

Por último, vamos a crear una clase de recursos.

ItemResource.java
package org.acme.quarkus.sample;

import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.Template;

@Path("item")
public class ItemResource {

    @Inject
    ItemService service;

    @Inject
    Template item; (1)

    @GET
    @Path("{id}")
    @Produces(MediaType.TEXT_HTML)
    public TemplateInstance get(Integer id) {
        return item.data("item", service.findItem(id)); (2)
    }
}
1 Inyecte la plantilla con la ruta templates/item.html.
2 Haga que el objeto Item sea accesible en la plantilla.

Métodos de extensión de plantillas

Los métodos de extensión de plantillas se utilizan para ampliar el conjunto de propiedades accesibles de los objetos de datos.

A veces, no tiene el control de las clases que desea utilizar en su plantilla y no puede añadirles métodos. Los métodos de extensión de plantillas le permiten declarar nuevos métodos para esas clases que estarán disponibles desde sus plantillas como si pertenecieran a la clase de destino.

Sigamos ampliando nuestra página HTML simple que contiene el nombre del artículo, el precio y añadamos un precio con descuento. El precio con descuento a veces se denomina "propiedad computada". Implementaremos un método de extensión de plantilla para renderizar esta propiedad fácilmente. Actualicemos nuestra plantilla:

HelloResource/item.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{item.name}</title>
</head>
<body>
    <h1>{item.name}</h1>
    <div>Price: {item.price}</div>
    {#if item.price > 100} (1)
    <div>Discounted Price: {item.discountedPrice}</div> (2)
    {/if}
</body>
</html>
1 if es una sección de flujo de control básico.
2 Esta expresión también se valida contra la clase Item y, obviamente, no hay tal propiedad declarada. Sin embargo, hay un método de extensión de plantilla declarado en la clase TemplateExtensions - véase más abajo.

Por último, vamos a crear una clase en la que pondremos todos nuestros métodos de extensión:

TemplateExtensions.java
package org.acme.quarkus.sample;

import io.quarkus.qute.TemplateExtension;

@TemplateExtension
public class TemplateExtensions {

    public static BigDecimal discountedPrice(Item item) { (1)
        return item.price.multiply(new BigDecimal("0.9"));
    }
}
1 Se puede utilizar un método de extensión de plantilla estática para añadir "propiedades computadas" a una clase de datos. La clase del primer parámetro se utiliza para hacer coincidir el objeto base y el nombre del método se utiliza para hacer coincidir el nombre de la propiedad.
Puede colocar métodos de extensión de plantillas en cada clase si los anota con @TemplateExtension, pero le aconsejamos que los mantenga agrupados por tipo de destino o en una única clase TemplateExtensions por convención.

Renderizado de informes periódicos

El motor de plantillas también puede ser muy útil para renderizar informes periódicos. Primero tendrá que añadir las extensiones quarkus-scheduler y quarkus-qute. En su archivo pom.xml, añada:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-qute</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-scheduler</artifactId>
</dependency>

Supongamos que tenemos un bean SampleService cuyo método get() devuelve una lista de muestras.

Sample.java
public class Sample {
    public boolean valid;
    public String name;
    public String data;
}

La plantilla es simple:

report.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Report {now}</title>
</head>
<body>
    <h1>Report {now}</h1>
    {#for sample in samples} (1)
      <h2>{sample.name ?: 'Unknown'}</h2> (2)
      <p>
      {#if sample.valid}
        {sample.data}
      {#else}
        <strong>Invalid sample found</strong>.
      {/if}
      </p>
    {/for}
</body>
</html>
1 La sección de bucle permite iterar sobre iterables, mapas y flujos.
2 Esta expresión de valor está utilizando el operador elvis - si el nombre es nulo se utiliza el valor por defecto.
ReportGenerator.java
package org.acme.quarkus.sample;

import jakarta.inject.Inject;

import io.quarkus.qute.Template;
import io.quarkus.qute.Location;
import io.quarkus.scheduler.Scheduled;

public class ReportGenerator {

    @Inject
    SampleService service;

    @Location("reports/v1/report_01") (1)
    Template report;

    @Scheduled(cron="0 30 * * * ?") (2)
    void generate() {
        String result = report
            .data("samples", service.get())
            .data("now", java.time.LocalDateTime.now())
            .render(); (3)
        // Write the result somewhere...
    }
}
1 En este caso, utilizamos el calificador @Location para especificar la ruta de la plantilla: templates/reports/v1/report_01.html.
2 Utilice la anotación @Scheduled para indicar a Quarkus que ejecute este método cada media hora. Para más información, consulte la guía del Programador.
3 El método TemplateInstance.render() desencadena la renderización. Tenga en cuenta que este método bloquea el hilo actual.

Guía de referencia Qute

Para saber más sobre Qute, consulte la guía de referencia de Qute.

Referencia de configuración de Qute

Propiedad de configuración fijada en tiempo de compilación - Todas las demás propiedades de configuración son anulables en tiempo de ejecución

Configuration property

Tipo

Por defecto

The list of suffixes used when attempting to locate a template file.

By default, engine.getTemplate("foo") would result in several lookups: foo, foo.html, foo.txt, etc.

Environment variable: QUARKUS_QUTE_SUFFIXES

Show more

list of string

qute.html, qute.txt, html, txt

The additional map of suffixes to content types. This map is used when working with template variants. By default, the java.net.URLConnection#getFileNameMap() is used to determine the content type of a template file.

Environment variable: QUARKUS_QUTE_CONTENT_TYPES__FILE_SUFFIX_

Show more

Map<String,String>

The list of exclude rules used to intentionally ignore some parts of an expression when performing type-safe validation.

An element value must have at least two parts separated by dot. The last part is used to match the property/method name. The prepended parts are used to match the class name. The value * can be used to match any name.

Examples:

  • org.acme.Foo.name - exclude the property/method name on the org.acme.Foo class

  • org.acme.Foo.* - exclude any property/method on the org.acme.Foo class

  • *.age - exclude the property/method age on any class

Environment variable: QUARKUS_QUTE_TYPE_CHECK_EXCLUDES

Show more

list of string

This regular expression is used to exclude template files found in template roots. Excluded templates are neither parsed nor validated during build and are not available at runtime.

The matched input is the file path relative from the root directory and the / is used as a path separator.

By default, the hidden files are excluded. The name of a hidden file starts with a dot.

Environment variable: QUARKUS_QUTE_TEMPLATE_PATH_EXCLUDE

Show more

Pattern

^\..*|.*\/\..*$

The prefix is used to access the iteration metadata inside a loop section.

A valid prefix consists of alphanumeric characters and underscores. Three special constants can be used:

  • <alias_> - the alias of an iterated element suffixed with an underscore is used, e.g. item_hasNext and it_count

  • <alias?> - the alias of an iterated element suffixed with a question mark is used, e.g. item?hasNext and it?count

  • <none> - no prefix is used, e.g. hasNext and count

By default, the <alias_> constant is set.

Environment variable: QUARKUS_QUTE_ITERATION_METADATA_PREFIX

Show more

string

The list of content types for which the ', ", <, > and & characters are escaped if a template variant is set.

Environment variable: QUARKUS_QUTE_ESCAPE_CONTENT_TYPES

Show more

list of string

text/html, text/xml, application/xml, application/xhtml+xml

The default charset of the templates files.

Environment variable: QUARKUS_QUTE_DEFAULT_CHARSET

Show more

Charset

UTF-8

The strategy used when multiple templates with the same path are found in the application.

Environment variable: QUARKUS_QUTE_DUPLICIT_TEMPLATES_STRATEGY

Show more

prioritizeIf multiple templates with the same path are found then determine the highest priority value and eliminate all templates with lowest priority. If there is exactly one template remaining then use this template. Otherwise, fail the build. Templates from the root application archive have the priority 30. Templates from other application archives have the priority 10. Templates from build items can define any priority., failFail the build if multiple templates with the same path are found.

prioritizeIf multiple templates with the same path are found then determine the highest priority value and eliminate all templates with lowest priority. If there is exactly one template remaining then use this template. Otherwise, fail the build. Templates from the root application archive have the priority {@code 30}. Templates from other application archives have the priority {@code 10}. Templates from build items can define any priority.

By default, a template modification results in an application restart that triggers build-time validations.

This regular expression can be used to specify the templates for which the application is not restarted. I.e. the templates are reloaded and only runtime validations are performed.

The matched input is the template path that starts with a template root, and the / is used as a path separator. For example, templates/foo.html.

Environment variable: QUARKUS_QUTE_DEV_MODE_NO_RESTART_TEMPLATES

Show more

Pattern

By default, the rendering results of injected and type-safe templates are recorded in the managed RenderedResults which is registered as a CDI bean.

Environment variable: QUARKUS_QUTE_TEST_MODE_RECORD_RENDERED_RESULTS

Show more

boolean

true

Enables or disables the Qute debug mode. This feature is experimental.

When enabled, Qute templates can be debugged directly at runtime. This includes the ability to:

  • Set breakpoints inside templates

  • Inspect the stack trace of visited template nodes during rendering

  • Evaluate expressions in the current Qute context

This mode is intended for development and troubleshooting purposes.

Default value: true

Example configuration:

quarkus.qute.debug.enabled = false

Environment variable: QUARKUS_QUTE_DEBUG_ENABLED

Show more

boolean

true

The strategy used when a standalone expression evaluates to a "not found" value at runtime and the quarkus.qute.strict-rendering config property is set to false

This strategy is never used when evaluating section parameters, e.g. {#if foo.name}. In such case, it’s the responsibility of the section to handle this situation appropriately.

By default, the NOT_FOUND constant is written to the output. However, in the development mode the PropertyNotFoundStrategy#THROW_EXCEPTION is used by default, i.e. when the strategy is not specified.

Environment variable: QUARKUS_QUTE_PROPERTY_NOT_FOUND_STRATEGY

Show more

defaultOutput the NOT_FOUND constant., noopNo operation - no output., throw-exceptionThrow a TemplateException., output-originalOutput the original expression string, e.g. {foo.name}.

Specify whether the parser should remove standalone lines from the output. A standalone line is a line that contains at least one section tag, parameter declaration, or comment but no expression and no non-whitespace character.

Environment variable: QUARKUS_QUTE_REMOVE_STANDALONE_LINES

Show more

boolean

true

If set to true then any expression that is evaluated to a Results.NotFound value will always result in a TemplateException and the rendering is aborted.

Note that the quarkus.qute.property-not-found-strategy config property is completely ignored if strict rendering is enabled.

Environment variable: QUARKUS_QUTE_STRICT_RENDERING

Show more

boolean

true

The global rendering timeout in milliseconds. It is used if no timeout template instance attribute is set.

Environment variable: QUARKUS_QUTE_TIMEOUT

Show more

long

10000

If set to true then the timeout should also be used for asynchronous rendering methods, such as TemplateInstance#createUni() and TemplateInstance#renderAsync().

Environment variable: QUARKUS_QUTE_USE_ASYNC_TIMEOUT

Show more

boolean

true

Related content

On the same extensions