Type-safe Templates
Type-safe Templates
There’s an alternate way to declare your templates in your Java itself, which relies on the following convention:
- Organize your template files in the
/src/main/resources/templates
directory by grouping them into one directory per resource class. For example, if yourItemResource
class references two templateshello
andgoodbye
, place them at/src/main/resources/templates/ItemResource/hello.txt
and/src/main/resources/templates/ItemResource/goodbye.txt
respectively. Grouping templates per resource class makes it easier to navigate to them. - In each of your resource classes, declare a
@CheckedTemplate static class Template {}
class within your resource class. - Declare one
public static native TemplateInstance
method per template file for your resource. - Use those static methods to build your template instances.
Creating a Simple Template
Create a directory to hold templates for our HelloResource
class (run this command from the root directory of the project):
mkdir -p src/main/resources/templates/HelloResource
Create a new file at src/main/resources/templates/HelloResource/hello.txt
and set the contents to be:
Hello {name} from HelloResource!
Warning: Note that thehello.txt
file in the previous step is located directly in thetemplates
directory. Since this example is using the alternate convention, you'll be creating another file with the same name in theHelloResource
subdirectory.
For the goodbye template, create a new file at src/main/resources/templates/HelloResource/goodbye.txt
and enter the contents:
Goodbye {name} from HelloResource!
Even though you created new template files for this new convention, the code will still exist in the HelloResource.java
file. Open that file (src/main/java/org/acme/HelloResource.java
) and replace the existing contents with the following:
package org.acme;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.CheckedTemplate;
@Path("hello")
public class HelloResource {
@CheckedTemplate(requireTypeSafeExpressions = false)
public static class Templates {
public static native TemplateInstance hello(String name);
public static native TemplateInstance goodbye(String name);
}
@GET
@Produces(MediaType.TEXT_PLAIN)
public TemplateInstance get(@QueryParam("name") String name) {
return HelloResource.Templates.hello(name);
}
}
A few notes on the updated code:
- This code expects to use a template at the path
templates/HelloResource/hello.txt
, since the@CheckedTemplate
static class is declared inside theHelloResource
class. The name of the method,hello
, is used to match files in the directory with common extensions like.txt
or.html
. You can specify an exact name and path using@ResourcePath
. - The
Templates.hello()
method returns a new template instance that can be customized before the actual rendering is triggered. In this case, you put the name value under the keyname
. The data map is accessible during rendering. - Again, notice that we don’t explicitly trigger the rendering - this is done automatically.
- Once you have declared a
@CheckedTemplate
class, Quarkus will check that all of its methods point to existing templates. As a result, if you try to use a template from your Java code and you forgot to add it, Quarkus will let you know at build time.
Keep in mind this style of declaration allows you to reference templates declared in other resources too.
Creating the Goodbye Resource
To fulfill the goodbye endpoint, you need to create a new resource.
src/main/java/org/acme/GoodbyeResource.java
and enter the following contents:
package org.acme;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.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.goodbye(name);
}
}
Testing the Endpoints
Let's start by testing the new implementation of the hello endpoint:
curl http://localhost:8080/hello?name=Daniel
You should see:
Hello Daniel from HelloResource!
curl http://localhost:8080/goodbye?name=Daniel
This time, you should see the goodbye template in use:
Goodbye Daniel from HelloResource!
As stated earlier, since the TemplateInstance
is declared as an inner class inside of HelloResource
, Qute will attempt to locate the template in the HelloResource/
subdirectory. If instead, you want to create a top-level declaration, you can do this inside a separate class (do not copy this code for this exercise):
@CheckedTemplate
public class Templates {
public static native TemplateInstance hello();
public static native TemplateInstance goodbye();
}
This will cause Qute to look for the associated hello.txt
or goodbye.txt
in the src/main/resources/templates
directory, instead of src/main/resources/templates/HelloResource
. It is up to you how you wish to organize your templates.