Currently Spring Boot allow one value for the Thymeleaf templates location with the spring.thymeleaf.prefix property.
The default value is classpath:/templates/.
I want to have another location for the thymeleaf templates (but keep the default one), outside the jar, for example:
spring.thymeleaf.prefix = classpath:/templates/, file:/resources/templates
Do i have to define another template resolver for the new location i want ?
Define the setting in the application.properties file
spring.thymeleaf.templateResolverOrder=1
Now in your custom Bean which creates ITemplateResolver set order to 0 along with prefix and suffix. This way spring boot will listen to both places
Setting order to 0 is important
An example of bean creation can be
#Bean
public ClassLoaderTemplateResolver emailTemplateResolver() {
ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver();
emailTemplateResolver.setPrefix("mails/");
emailTemplateResolver.setSuffix(".html");
emailTemplateResolver.setTemplateMode(TemplateMode.HTML);
emailTemplateResolver.setCharacterEncoding("UTF-8");
emailTemplateResolver.setOrder(0);
emailTemplateResolver.setCheckExistence(true);
return emailTemplateResolver;
}
MyExample
In order to define multiple template locations, you must define Spring beans implementing ITemplateResolver.
#Bean
public SpringResourceTemplateResolver firstTemplateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("classpath:/templates/templatelocation/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setOrder(0);
templateResolver.setCheckExistence(true);
return templateResolver;
}
#Bean
public ClassLoaderTemplateResolver secondTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/templatelocation/other/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setOrder(1);
templateResolver.setCheckExistence(true);
return templateResolver;
}
You can also check out the blog post detailing the usage.
Related
I'm tryting to add externalized messages from a properties file in a thymeleaf html template. I have a file messages_es.properties located in i18n/messages with this inside:
estimado=Estimado:
In my html template i have the following
<div th:text="#{estimado}">Estimado placeholder</div>
but then, the html I get is the following:
<div>??estimado_es??</div>
Is anyone able to help me?
This is the configuration i have added in order to make this work:
#Bean
#Qualifier("custom")
public ITemplateResolver thymeleafTemplateResolver(){
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
#Bean
public SpringTemplateEngine thymeleafTemplateEngine(#Qualifier("custom") ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
templateEngine.setTemplateEngineMessageSource(messageSource());
return templateEngine;
}
#Bean
#Description("Spring Message Resolver")
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("i18n/messages");
return messageSource;
}
In my application.yml file:
spring.messages.basename: i18n/messages
first of all you should add classpath: as prefix to your base name.
secondly if you have baseName set toi18n/messages app will look for i18n/messages_es.properties
if i understand correctly your file path is i18n/messages/messages_es.properties
so you need to set baseName to: classpath:i18n/messages/messages
I have added a second TemplateResolver because I would like to use also Thymeleaf to serve Template for Plain-Text Templates. When starting application it works fine, the Thymeleaf templates are being resolved.
But from now my #WebMvcTests are not working. I says, that:
Error resolving template [disclaimer/view], template might not exist or might not be accessible by any of the configured Template Resolvers
The TemplateResolver are configures like:
#Configuration
public class SitemapViewResolverConfiguration {
#Bean
public ITemplateResolver thymeleafTextTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("/templates/sitemap/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".txt");
templateResolver.setTemplateMode(TemplateMode.TEXT);
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setCheckExistence(TRUE);
return templateResolver;
}
}
#Configuration
public class PageViewResolverConfiguration {
#Bean
public ITemplateResolver thymeleafHtmlTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("classpath:/templates/view/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setCheckExistence(TRUE);
return templateResolver;
}
}
My template structure is as follows:
The test class is:
#RunWith(SpringRunner.class)
#WebMvcTest(DisclaimerController.class)
public class DisclaimerControllerTest {
#Autowired MockMvc mvc;
#Test
public void testGet() throws Exception {
mvc.perform(get("/disclaimer")
.andExpect(status().isOk())
.andExpect(view().name("disclaimer/view"))
;
}
}
Error:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template [disclaimer/view], template might not exist or might not be accessible by any of the configured Template Resolvers
Okay, I found the problem. As I use #WebMvcTest it didn't load my TemplateResolver. As before I relied on Spring Boot automatic resolver, I am using now two selfmade resolver. But they are not picked up by the test, that's why MockMvc could not resolve it.
I fixed it by adding Configuration as Import:
#RunWith(SpringRunner.class)
#WebMvcTest({PageViewResolverConfiguration.class, DisclaimerController.class})
public class DisclaimerControllerTest {
...
}
I'm trying to cofigure my project. I have two variants of tempalteResolver.
if i use this one:
#Bean
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(applicationContext);
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCacheable(true);
return templateResolver;
}
i get an error that my template can't resolve the thymeleaf syntax th even when i add xmlns:th="http://www.thymeleaf.org"
and when i use the following code:
#Bean
public ClassLoaderTemplateResolver templateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCacheable(false);
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
i get an error that there is no templates under this path (/WEB-INF/templates).
Full path to my templates is src\main\webapp\WEB-INF\templates\
What is the best template resolver to be used and why?
Thanks!
You should be using SpringResourceTemplateResolver for web projects (using SpringMvc controllers), and ClassLoaderTemplateResolver for command line projects, or when you need to generate html for non-web applications (such as sending html email or something like that).
First error occured becouse spring security somehow didn't connect to a project and my
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
modelAndView.addObject("auth", auth);
Send nulls.
And second error i dunno why occured.
I created a module in a Spring-boot project and I want to redirect a controller to a view in this module. I can't find a solution and I've been trying different options. Now, I'm adding template resolvers in my WebMvcConfig and the one to the root templates is working but I can't make the other one work. Any idea please?
Here is the WebMvcConfig:
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Bean
public ClassLoaderTemplateResolver templateModuleResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("espmonitor/src/main/resources/templates/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
#Bean
public ClassLoaderTemplateResolver templateRootResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addTemplateResolver(templateModuleResolver());
templateEngine.addTemplateResolver(templateRootResolver());
return templateEngine;
}
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setCharacterEncoding("UTF-8");
return viewResolver;
}
The view controller is:
#RequestMapping("/index_esp_monitor")
public String espMonitor(Model model){
return "index_esp_monitor";
}
My project structure:
I managed to make it work and I'm going to show the solution here just in case someone is trying to do a similar thing.
I was trying to have many applications within one project and trying to have each application in a different module.
First, I created the modules in the project and add an additional module as a selector. Then I remove the src folder from the project and add the modules to the project pom
<modules>
<module>espmonitor</module>
<module>seed</module>
<module>selector</module>
</modules>
And the pom of each module contains:
<parent>
<artifactId>SeedCert</artifactId>
<groupId>com.niab</groupId>
<version>0.0.1</version>
</parent>
The project structure is as follows, the module selector will contain the indexes to the other modules.
The Application.java is configured to scan the configurations of the other modules.
#ComponentScan({
"com.niab.config",
"com.niab.selector.controller"
})
Each module has a com.niab.config package for the specific scanning
I'm not sure if this is the most elegant way of doing it, but it's quite a simple solution and it does what I need.
I want Spring look for templates in different locations, not only in src/main/resource/templates/.
Here what I have
#Configuration
public class WebConfig{
#Autowired
private SpringTemplateEngine templateEngine;
#PostConstruct
public void clientTemplate(){
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix("static/client");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setOrder(templateEngine.getTemplateResolvers().size());
resolver.setCacheable(false);
templateEngine.addTemplateResolver(resolver);
}
}
But when I try to access a existing template index.html in /src/main/resources/static/client, I've got:
java.lang.IllegalArgumentException: ApplicationContext has not been initialized in resource resolver. TemplateResolver or ResourceResolver might not have been correctly configured by the Spring Application Context.
at org.thymeleaf.util.Validate.notNull(Validate.java:37)
at org.thymeleaf.spring4.resourceresolver.SpringResourceResourceResolver.getResourceAsStream(SpringResourceResourceResolver.java:81)
at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:221)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1104)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011)
at org.thymeleaf.spring4.view.ThymeleafView.renderFragment(ThymeleafView.java:335)
at org.thymeleaf.spring4.view.ThymeleafView.render(ThymeleafView.java:190)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1257)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
How to resolve this problem?
1. Non-Spring managed SpringResourceTemplateResolver
With your current setup, you are creating an instance of SpringResourceTemplateResolver, NOT in a #Bean method. Thus, it doesn't get picked up by spring and you need to provide the ApplicationContext (example):
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/WEB-INF/templates/");
resolver.setTemplateMode(TemplateMode.HTML);
Notice the resolver.setApplicationContext(applicationContext);
2. Spring managed SpringResourceTemplateResolver
You are probably seeing examples which don't need do that, but they are most certainly creating the SpringResourceTemplateResolver instance in a #Bean method. Spring picks it up and sets the application context for you (example):
#Bean
public ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix("/WEB-INF/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCacheable(false);
return resolver;
}
ThymeLeaf trips if the template resolver is not defined as a #Bean. You can replace your WebConfig class with this one.
#Configuration
public class WebConfig{
#Bean
public ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setPrefix("static/client");
// For Spring Boot
// resolver.setPrefix("classpath:/templates/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setCacheable(false);
return resolver;
}
}
The new template will be picked up and added to the list of template resolvers, effectively doing the same operations as "templateEngine.addTemplateResolver()" would do.