Johna
Johna

Reputation: 1894

Spring 4 + Thymeleaf 3 templates not found when placed outside WEB-INF folder

I have following folder structure in my spring-boot project.

<prject>
- main
--java
--resources
---static (css, js, images etc)
---templates (html files)

I get file not found exception with related to the template file like below.

java.io.FileNotFoundException: Could not open ServletContext resource [/templates/user/form.html]

My POM file is as below

<packaging>jar</packaging>
...
<dependencies>
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf</artifactId>
            <version>3.0.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring4</artifactId>
            <version>3.0.3.RELEASE</version>
        </dependency>
...
</dependencies>

And, my configuration class is as below:

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/static/");
    }

    private ApplicationContext applicationContext;

    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }


    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(true).
                favorParameter(false).
                ignoreAcceptHeader(true).
                useJaf(false).
                defaultContentType(MediaType.APPLICATION_JSON);
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof MappingJackson2HttpMessageConverter) {
                MappingJackson2HttpMessageConverter jsonMessageConverter = (MappingJackson2HttpMessageConverter) converter;
                ObjectMapper objectMapper = jsonMessageConverter.getObjectMapper();
                objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
                break;
            }
        }
    }

    @Bean
    public ObjectMapper objectMapper()
    {
        ObjectMapper mapper = new ObjectMapper();
        mapper.findAndRegisterModules();
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
        return mapper;
    }


    @Bean
    public ITemplateResolver templateResolver() {
        SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
        templateResolver.setApplicationContext(applicationContext);
        templateResolver.setPrefix("/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode(TemplateMode.HTML);

        return templateResolver;
    }

    @Bean
    public TemplateEngine templateEngine() {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setEnableSpringELCompiler(true);
        templateEngine.setTemplateResolver(templateResolver());
        templateEngine.setTemplateEngineMessageSource(messageSource());
        templateEngine.addDialect(new LayoutDialect());
        return templateEngine;
    }


    @Bean
    public ViewResolver viewResolver() {
        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(templateEngine());
        viewResolver.setOrder(1);
        return viewResolver;
    }

    @Bean
    public ResourceBundleMessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        return messageSource;
    }

}

It works when templates are placed in webapp/WEB-INF and template resolvers prefix is set to it.

It also works when the spring-boot-starter-thymeleaf decency is used instead of the two thyme leaf dependencies I have used here and configuration is completely removed from the MvcConfig file.

Can you show me a way to get this working with the same folder structure and dependencies.

Upvotes: 1

Views: 2006

Answers (2)

Anas Elbenney
Anas Elbenney

Reputation: 53

I added classpath: to the prefix and it worked for me: emailTemplateResolver.setPrefix("classpath:/templates/");

Upvotes: 3

Edgar Conrado
Edgar Conrado

Reputation: 196

As @Minjun Yu say, I can fix it adding templateResolver.setPrefix("/resources/templates/") in my configuration.

Regards

Upvotes: 1

Related Questions