Template de uso do Qute
O Qute é um mecanismo de criação de modelos desenvolvido especificamente para o Quarkus. O uso de reflections é minimizado para reduzir o tamanho das imagens nativas. A API combina o estilo de codificação imperativa e o estilo reativo sem bloqueio. No modo de desenvolvimento, todos os arquivos localizados em src/main/resources/templates são monitorados quanto a alterações, e as modificações tornam-se visíveis imediatamente. Além disso, nosso objetivo é detectar a maioria dos problemas de modelo no momento da compilação. Neste guia, você aprenderá a renderizar facilmente os 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.
Disponibilizando os templates do Qute via HTTP
Se você quiser disponibilizar seus templates via HTTP:
-
A extensão Qute Web permite que você veja diretamente os templates HTTP localizados em
src/main/resources/templates/pub/. Nesse caso, não é necessário nenhum código Java para "conectar" o template; por exemplo, o templatesrc/main/resources/templates/pub/foo.htmlserá exibido a partir dos caminhos/fooe/foo.htmlpor padrão. -
Para um controle mais preciso, é possível combiná-lo com o Quarkus REST para controlar como o template será exibido. Todos os arquivos localizados no diretório
src/main/resources/templatese seus subdiretórios são registrados como templates e podem ser injetados em um recurso REST.
<dependency>
<groupId>io.quarkiverse.qute.web</groupId>
<artifactId>quarkus-qute-web</artifactId>
</dependency>
implementation("io.quarkiverse.qute.web:quarkus-qute-web")
| The Qute Web extension, while hosted in the Quarkiverse, is part of the Quarkus Platform and its version is defined in the Quarkus Platform BOM. |
Exibindo o Hello World com o Qute
Let’s start with a Hello World template:
<h1>Hello {http:param('name', 'Quarkus')}!</h1> (1)
| 1 | {http:param('name', 'Quarkus')} é uma expressão que é exibida quando o modelo é renderizado (Quarkus é o valor padrão). |
Os templates localizados no diretório pub são exibidos via HTTP. Esse comportamento é incorporado, não é necessário nenhum controller. Por exemplo, o template src/main/resources/templates/pub/foo.html será exibido a partir dos caminhos /foo e /foo.html por padrão.
|
Quando o aplicativo estiver em execução, você pode abrir o navegador e navegar para: http://localhost:8080/hello?name=Martin
For more information about Qute Web options, see the Qute Web guide.
Hello Qute and REST
Para um controle mais refinado, você pode combinar o Qute Web com o Quarkus REST (antigo RESTEasy Reactive) ou com a extensão baseada no RESTEasy Classic para controlar como o seu template será exbido
Using the quarkus-rest-qute extension:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-qute</artifactId>
</dependency>
implementation("io.quarkus:quarkus-rest-qute")
A very simple text template:
Hello {name}! (1)
| 1 | {name} é uma expressão de valor que é avaliada quando o modelo é processado. |
Agora vamos injetar o modelo "compilado" na classe de recurso.
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 template. Neste caso específico, vamos injetar um template com o caminho templates/hello.txt. |
| 2 | O Template.data() retorna uma nova instância do template que pode ser configurado antes da renderização. Neste caso, colocamos o valor do nome na chave name. O mapa de dados é acessível durante a renderização. |
| 3 | Note that we don’t trigger the rendering - this is done automatically by a special ContainerResponseFilter implementation provided by quarkus-rest-qute. |
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 seus arquivos de template no diretório
/src/main/resources/templates, agrupando-os em um diretório por classe de recurso. Portanto, se a classeFruitResourcefizer referência a dois templates:appleseoranges, coloque-os em/src/main/resources/templates/FruitResource/apples.txte/src/main/resources/templates/FruitResource/oranges.txt. O agrupamento de templates 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 template para o seu recurso. -
Utilize esses métodos estáticos para criar as suas instâncias de template.
Aqui está o exemplo anterior, reescrito com este estilo:
Vamos começar com um modelo muito simples:
Hello {name}! (1)
| 1 | {name} é uma expressão de valor que é avaliada quando o modelo é processado. |
Now let’s declare and use this template in the resource class.
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 do template que é retornada do método resource. Observe que não acionamos a renderização - isso é feito automaticamente por uma implementação especial do ContainerResponseFilter fornecida pelo quarkus-rest-qute . |
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 template no seu projeto :)
|
Não se esqueça de que este estilo de declaração também permite fazer referência a templates declarados em outros recursos:
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);
}
}
Templates e Níveis superiores.
Naturalmente, se quiser declarar os templates a um nível acima, você pode por exemplo, declará-los numa classe Templates de nível acima (não aninhada) diretamente em: /src/main/resources/templates/hello.txt
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 template com o caminho templates/hello. |
Declarações de parâmetros de modelo
Se você declarar uma declaração de parâmetro em um template, 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:
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 template:
<!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:
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 template. |
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 template dentro do próprio template
Em alternativa, pode declarar os seus parâmetros de template no próprio arquivo de template.
Vamos começar de novo com o template:
{@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.
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 template com o caminho templates/item.html. |
| 2 | Tornar o objeto Item acessível no template. |
Métodos de extensão de templates
Os métodos de extensão de templates são utilizados para estender o conjunto de propriedades acessíveis dos objetos de dados.
Às vezes, o usuário não tem controle sobre as classes que deseja usar no template e não pode adicionar métodos a elas. Os métodos de extensão de template permitem que você declare novos métodos para essas classes que estarão disponíveis nos templates, 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 template para processar esta propriedade facilmente. Vamos atualizar o nosso template:
<!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 template declarado na classe TemplateExtensions - veja abaixo. |
Finalmente, vamos criar uma classe onde colocamos todos os nossos métodos de extensão:
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 template 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 templates 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 templates também pode ser muito útil para renderizar relatórios periódicos. Primeiro, você precisará adicionar as extensões quarkus-scheduler e quarkus-qute . Em 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.
public class Sample {
public boolean valid;
public String name;
public String data;
}
O modelo é simples:
<!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. |
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 template: 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, Environment variable: Show more |
list of string |
|
The additional map of suffixes to content types. This map is used when working with template variants. By default, the Environment variable: 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 Examples:
Environment variable: 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 By default, the hidden files are excluded. The name of a hidden file starts with a dot. Environment variable: Show more |
|
|
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:
By default, the Environment variable: Show more |
string |
|
The list of content types for which the Environment variable: Show more |
list of string |
|
The default charset of the templates files. Environment variable: Show more |
|
|
The strategy used when multiple templates with the same path are found in the application. Environment variable: Show more |
|
|
Use the equals character as a command to identify an output expression in all templates from the application template
root, i.e. By default, no special command is used, i.e. the syntax for an output expression looks like Environment variable: Show more |
booleano |
|
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 Environment variable: Show more |
||
By default, the rendering results of injected and type-safe templates are recorded in the managed Environment variable: Show more |
booleano |
|
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:
This mode is intended for development and troubleshooting purposes. Default value: Example configuration:
Environment variable: Show more |
booleano |
|
The strategy used when a standalone expression evaluates to a "not found" value at runtime and the This strategy is never used when evaluating section parameters, e.g. By default, the Environment variable: Show more |
|
|
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: Show more |
booleano |
|
If set to Note that the Environment variable: Show more |
booleano |
|
The global rendering timeout in milliseconds. It is used if no Environment variable: Show more |
long |
|
If set to Environment variable: Show more |
booleano |
|