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

Motor de template Qute

Qute é um mecanismo de modelagem projetado especificamente para atender às necessidades do Quarkus. O uso de reflexão é minimizado para reduzir o tamanho das imagens nativas. A API combina tanto o estilo imperativo quanto o estilo de codificação reativo e não bloqueante. No modo de desenvolvimento, todos os arquivos localizados em src/main/resources/templates são monitorados para alterações, e as modificações são imediatamente visíveis. Além disso, tentamos detectar a maioria dos problemas de modelo durante o tempo de compilação. Neste guia, você aprenderá como renderizar facilmente modelos em sua aplicação.

Solução

Recomendamos que siga as instruções nas seções seguintes e crie a aplicação passo a passo. No entanto, você pode ir diretamente para o exemplo completo.

Clone o repositório Git: git clone https://github.com/quarkusio/quarkus-quickstarts.git, ou baixe um arquivo.

A solução está localizada no diretório qute-quickstart.

Hello World com Jakarta REST

Se quiser utilizar o Qute na sua aplicação Jakarta REST, tem de adicionar primeiro uma extensão:

  • either quarkus-rest-qute if you are using Quarkus REST (formerly RESTEasy Reactive):

    pom.xml
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-rest-qute</artifactId>
    </dependency>
    build.gradle
    implementation("io.quarkus:quarkus-rest-qute")
  • ou quarkus-resteasy-qute se estiver utilizando o RESTEasy Clássico:

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

Vamos começar com um modelo muito simples:

hello.txt
Hello {name}! (1)
1 {name} é uma expressão de valor que é avaliada quando o modelo é processado.
Por padrão, todos os arquivos localizados no diretório src/main/resources/templates e seus subdiretórios são registrados como modelos. Os modelos são validados durante o início da aplicação e monitorados para alterações no modo de desenvolvimento.

Agora vamos injetar o modelo "compilado" na classe de recurso.

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 Se não for fornecido um qualificador @Location, o nome do campo é utilizado para localizar o modelo/template. Neste caso específico, vamos injetar um modelo com o caminho templates/hello.txt.
2 O Template.data() retorna uma nova instância de modelo que pode ser configurado antes que a renderização. Neste caso, colocamos o valor do nome na chave name. O mapa de dados está acessível durante a renderização.
3 Note-se que não acionamos a renderização - isto é feito automaticamente por uma implementação especial do ContainerResponseFilter.

Se sua aplicação estiver rodando, você pode fazer uma requisição ao endpoint:

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

Type-safe templates(Templates com tipos seguros)

Existe uma forma alternativa de declarar os seus modelos no seu código Java, que se baseia na seguinte convenção:

  • Organize os seus arquivos de modelos no diretório /src/main/resources/templates, agrupando-os em um diretório por classe de recurso. Assim, se a sua classe ItemResource faz referência a dois modelos hello e goodbye, coloque-os em /src/main/resources/templates/ItemResource/hello.txt e /src/main/resources/templates/ItemResource/goodbye.txt. O agrupamento de modelos por classe de recurso facilita a navegação até eles.

  • Em cada uma das suas classes de recursos, declare uma classe @CheckedTemplate static class Template {} dentro da sua classe de recursos.

  • Declare um public static native TemplateInstance method(); por arquivo modelo para o seu recurso.

  • Utilize esses métodos estáticos para criar as suas instâncias de modelo.

Aqui está o exemplo anterior, reescrito com este estilo:

Vamos começar com um modelo muito simples:

HelloResource/hello.txt
Hello {name}! (1)
1 {name} é uma expressão de valor que é avaliada quando o modelo é processado.

Agora vamos declarar e utilizar esses modelos na classe de recurso.

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 Isto declara um modelo com o caminho templates/HelloResource/hello.
2 Templates.hello() retorna uma nova instância de modelo que é gerada pelo método de recurso. Observe que não acionamos a renderização - isso é feito automaticamente por uma implementação especial de ContainerResponseFilter.
Depois de declarar uma classe @CheckedTemplate, verificaremos se todos os seus métodos apontam para modelos existentes, por isso, informaremos no tempo de compilação, caso tenha esquecido de adicionar um modelo no seu projeto :)

Não se esqueça de que este estilo de declaração também permite fazer referência a modelos declarados em outros 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;

@Path("goodbye")
public class GoodbyeResource {

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

Top-level type-safe templates

Naturalmente, se quiser declarar os modelos ao nível superior, diretamente em /src/main/resources/templates/hello.txt, por exemplo, pode declará-los numa classe Templates de nível superior (não aninhada):

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 Isto declara um modelo com o caminho templates/hello.

Declarações de parâmetros de modelo

Se você declarar uma declaração de parâmetro em um modelo, o Qute tentará validar todas as expressões que fazem referência a esse parâmetro e, se uma expressão incorreta for encontrada, a compilação falhará.

Vamos supor que temos uma classe simples como esta:

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

E gostaríamos de renderizar uma página HTML simples que contenha o nome e o preço do item.

Vamos começar de novo com o modelo:

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 expressão é validada. Se tentar alterar a expressão para {item.nonSense}, a construção deverá falhar.
2 Isto também é validado.

Finalmente, vamos criar uma classe de recurso com modelos type-safe:

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 um método que nos dê um TemplateInstance para templates/ItemResource/item.html e declare o seu parâmetro Item item para podermos validar o modelo.
2 Tornar o objeto Item acessível no modelo.
When the --parameters compiler argument is enabled, Quarkus REST may infer the parameter names from the method argument names, making the @PathParam("id") annotation optional in this case.

Declaração do parâmetro do modelo dentro do próprio modelo

Em alternativa, pode declarar os seus parâmetros de modelo no próprio arquivo de modelo.

Vamos começar de novo com o modelo:

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 Declaração de parâmetro opcional. O Qute tenta validar todas as expressões que fazem referência ao parâmetro item.
2 Esta expressão é validada. Se tentar alterar a expressão para {item.nonSense}, a construção deverá falhar.

Finalmente, vamos criar uma classe de recurso.

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 Injetar o modelo com o caminho templates/item.html.
2 Tornar o objeto Item acessível no modelo.

Métodos de extensão de modelos

Os métodos de extensão de modelos são utilizados para estender o conjunto de propriedades acessíveis dos objetos de dados.

Por vezes, você não tem controle sobre as classes que deseja usar em seu modelo e não pode adicionar métodos às mesmas. Os métodos de extensão de modelos permitem-lhe declarar novos métodos para essas classes que estarão disponíveis a partir dos seus modelos, tal como se pertencessem à classe de destino.

Vamos continuar a estender a nossa página HTML simples que contém o nome do item, o preço e adicionar um preço com desconto. O preço com desconto é por vezes designado por “propriedade computada”. Vamos implementar um método de extensão de modelo para processar esta propriedade facilmente. Vamos atualizar o nosso modelo:

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 é uma instrução básica do fluxo de controle.
2 Esta expressão é também validada em relação à classe Item e, obviamente, não existe tal propriedade declarada. No entanto, existe um método de extensão de modelo declarado na classe TemplateExtensions - veja abaixo.

Finalmente, vamos criar uma classe onde colocamos todos os nossos métodos de extensão:

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 Um método de extensão de modelo estático pode ser utilizado para adicionar “propriedades computadas” a uma classe de dados. A classe do primeiro parâmetro é utilizada para corresponder ao objeto base e o nome do método é utilizado para corresponder ao nome da propriedade.
pode colocar métodos de extensão de modelos em todas as classes se os anotar com @TemplateExtension, mas aconselhamos a mantê-los agrupados por tipo de destino ou numa única classe TemplateExtensions por convenção.

Renderização de relatórios periódicos

O mecanismo de criação de modelos também pode ser muito útil na apresentação de relatórios periódicos. Primeiro, é necessário adicionar as extensões quarkus-scheduler e quarkus-qute. No seu arquivo pom.xml, adicione:

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

Vamos supor que temos um bean SampleService cujo método get() devolve uma lista de amostras.

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

O modelo é simples:

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 A instrução de repetição permite iterar sobre iteráveis, mapas e fluxos.
2 Esta expressão de valor está utilizando o operador el vis - se o nome for nulo, é utilizado o valor predefinido.
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 Neste caso, utilizamos o qualificador @Location para especificar o caminho do modelo: templates/reports/v1/report_01.html.
2 Utilize a anotação @Scheduled para instruir o Quarkus a executar este método a cada meia hora. Para mais informações, consulte o guia Scheduler.
3 O método TemplateInstance.render() aciona a renderização. Note-se que este método bloqueia a thread atual.

Guia de Referência do Qute

Para saber mais sobre a Qute, consulte o guia de referência da Qute.

Referência de configuração do Qute

Propriedade de Configuração Fixa no Momento da Compilação - Todas as outras propriedades de configuração podem ser sobrepostas em tempo de execução.

Configuration property

Tipo

Padrão

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 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 from the templates directory. 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 templates 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

<alias_>

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

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

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

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

Show more

Map<String,String>

Conteúdo Relacionado

Nas mesmas extensões

Nos mesmos tópicos