4
votes

I have the following thymeleaf html page:

<head th:fragment="header">

    <meta charset="utf-8" />
    <link rel="stylesheet" href="../../css/main.css" th:href="@{/css/main.css}" />
    <title th:text="#{device.page.title}">Title</title>
</head>

<body>
<div>
    <h1 th:text="#{device.table.caption}"></h1>
    <hr class="fineline"/>
    Select devices using the checkboxes, you can update the client version or add client commands.
    <form action="#" th:action="@{/devices/modify}" th:object="${deviceCommand}" method="post">
    <table border="0" cellpadding="0" cellspacing="0" class="touchTable">
        <!--<thead> -->
            <tr>
                <td scope="col" th:text="#{device.check.label}">Select</td>
                <td width="300" scope="col"><span th:text="#{device.id.label}"></span>&nbsp;(<span th:text="#{device.retailer.name.label}"></span>)</td>
                <td scope="col" th:text="#{device.current.label}">Curr Version</td>
                <td scope="col" th:text="#{device.next.label}">Next Version</td>
                <td scope="col" th:text="#{device.commands.label}">Commands</td>
            </tr>
        <!--</thead>-->
        <!--<tbody> -->
            <tr th:each="d : ${devices}">
                <td><input type="checkbox" th:field="*{deviceModificationIds}" th:value="${d.id}"/></td>
                <td><span th:text="${d.id}"></span>&nbsp;(<span th:text="${d.retailerName}"></span>)</td>
                <td th:text="${d.currentClientVersion}">Washington</td>
                <td th:text="${d.nextClientVersion}">gwash</td>
                <td th:text="${d.commands}">gwash</td>
            </tr>
            <tr>
                <td colspan="2"></td>
                <td><span th:text="#{device.change.version.label}"></span><br/><input type="text" th:field="*{newVersion}"/></td>
                <td><span th:text="#{device.add.command.label}"></span><br/><input type="text" th:field="*{newCommand}"/></td>
                <td><br/><button type="submit" th:text="#{device.modify.action.button}">Action</button></td>
            </tr>
        <!--</tbody>  -->
    </table>
    </form>


</div>
</body>

The problem is with the css stylesheet. Basically spring doesn't seem to be able to find it, despit me placing the file in /resources/static/css/main.css

It returns the error (in the logs):

o.s.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/css/main.css] in DispatcherServlet with name 'dispatcherServlet'

Now the doco all says that Spring Boot should automatically serve things in /resources/static

Here is my WebConfig:

@Configuration
@ComponentScan("com.txxxxcorp.txxxxpoint.resource")
@EnableWebMvc
public class WebConfig {

    @Bean
    MultipartConfigElement multipartConfigElement() {
        MultiPartConfigFactory factory = new MultiPartConfigFactory();
        factory.setMaxFileSize("4096KB");
        factory.setMaxRequestSize("4096KB");
        return factory.createMultipartConfig();
    }

    @Bean  
    public ViewResolver viewResolver() {
        ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        templateResolver.setTemplateMode("XHTML");
        templateResolver.setPrefix("templates/");
        templateResolver.setSuffix(".html");

        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver);

        ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
        viewResolver.setTemplateEngine(engine);

        String[] excludedViews = new String[]{
            "/resources/static/**"};
        viewResolver.setExcludedViewNames(excludedViews);

        return viewResolver;
    }

    @Bean
    public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
        return new EmbeddedServletContainerCustomizer() {
            @Override
            public void customize(ConfigurableEmbeddedServletContainer servletContainer) {
                ((TomcatEmbeddedServletContainerFactory) servletContainer).addConnectorCustomizers(
                        new TomcatConnectorCustomizer() {
                            @Override
                            public void customize(Connector connector) {
                                AbstractHttp11Protocol httpProtocol = (AbstractHttp11Protocol) connector.getProtocolHandler();
                                httpProtocol.setCompression("on");
                                httpProtocol.setCompressionMinSize(256);
                                String mimeTypes = httpProtocol.getCompressableMimeTypes();
                                String mimeTypesWithJson = mimeTypes + "," + MediaType.APPLICATION_JSON_VALUE;
                                httpProtocol.setCompressableMimeTypes(mimeTypesWithJson);
                            }
                        }
                );
            }
        };
    }

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

For some reason, the editor does not let me insert the spring security config without complaining that its not properly formatted (IntelliJ, Maven and Spring Boot disagree with this, since it compiles and works), rest assured that I have allowed the /css/main.css path to go through

Does anyone know why I cannot resolve the css file?

3
Is there some specific reason that you are not using @EnableAutoConfiguration and you are configuring Spring MVC on your own? I am pretty sure that Spring Boot is not serving the static content because you have all the auto configuration has been disabled - geoand
I have @EnableAutoConfiguration on the Application starter, I thought that this was the only place you needed it? - Michael Coxon
Ok I see. However the fact that you have @EnableWebMvc will disable MVC autoconfiguration. See docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/… - geoand
aha! - that seems to have fixed this issue. I hope I haven't broken something else by removing @EnableWebMvc ... - Michael Coxon
I have added an answer for future readers to easily see immediately without having to read the comments. Please accept it if you think it's correct - geoand

3 Answers

11
votes

The fact that you have used @EnableWebMvc turns off Spring Boot's MVC autoconfiguration (and therefor the static resource handling).

To enable the static resource handling the best solution is to remove @EnableWebMvc and let Spring Boot do what it does best - auto-configuration.

After the change you should do some regression testing to make sure nothing else broke

1
votes

@geoand's Answer is correct ,your application's static resource is turn off as you use the @enablWebMvc . so any css file you used here been parsed by thyemeleaf ,you need to declare the class to inheritted the WebMvcAutoConfigurationAdapter and override the addResourceHandlers method, the code could be like below :

 @Configuration
    @ComponentScan("com.txxxxcorp.txxxxpoint.resource")
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurerAdapter {

 @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        String[] STATIC_RESOURCE = {"/","classpath:/","classpath:/META-INF/resources/", "classpath:/META-INF/resources/webjars/",
                "classpath:/resources/", "classpath:/static/", "classpath:/public/"};

        if (!registry.hasMappingForPattern("/**")) {
            registry.addResourceHandler("/**").addResourceLocations(STATIC_RESOURCE);
        }
    }

.......................
}
0
votes

I had to add thymeleaf dependency to pom. Without this dependency Spring Boot didn't found static resources.

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>