Question

The recent blog post (https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot) by Spring regarding the use of static web content in Spring Boot projects indicates that several resource directories may be used:

  • /META-INF/resources/
  • /resources/
  • /static/
  • /public/

This is thanks to the WebMvcAutoConfiguration class which automatically adds these directories to the classpath. This all seems fine and appears to work when using the spring-boot-maven-plugin spring-boot:run goal, all of your static content is working (eg: /index.html).

When you package your Spring Boot project and allow the spring-boot-maven-plugin to create the enhanced JAR then attempt to run your project using java -jar my-spring-boot-project.jar you find that your static content now returns a 404 error.

Was it helpful?

Solution

It turns out that whilst Spring Boot is being clever at adding the various resource directories to the classpath, Maven is not and it's up to you to deal with that part. By default, only src/main/resources will be included in your JAR. If you create a folder called /static in the root of your project (as implied by the blog post) then it will work fine whilst using the spring-boot:run Maven goal but not once you create a JAR.

The easiest solution is to create your /static folder inside /src/main/resources and then Maven will include it in the JAR. Alternative you can add additional resource locations to your Maven project:

<resources>
    <resource>
        <directory>src/main/resources</directory>
    </resource>
    <resource>
        <directory>static</directory>
        <targetPath>static</targetPath>
    </resource>
</resources>

I hope this is useful to someone, it's kind of obvious when you step back and look at how Maven works but it might stump a few people using Spring Boot as it's designed to be pretty much configuration free.

OTHER TIPS

I am banging my head against the wall trying to figure out how to do this with gradle. Any tips?

EDIT: I got it to work by adding this to my build.gradle:

// Copy resources into the jar as static content, where Spring expects it.
jar.into('static') {
    from('src/main/webapp')
}

I was going around few pages to understand how to serve static content in Spring boot environment. Mostly all advises was around placing the static files with in /static /resources/ src/main/webapp etc. Thought of sharing below approach.

  1. Allow spring boot to auto configure Dispatcher Servlet - Make sure DispatcherServletAutoConfiguration is not in the exclude for AutoConfiguration.

    @EnableAutoConfiguration(exclude = { //DispatcherServletAutoConfiguration.class, })

  2. Inject your external directory for static content routing

    @Value("${static-content.locations:file:C:/myprj/static/") private String[] staticContentLocations;

3.Override WebMvcAutoConfiguration using WebMvcConfigurerAdapter to advice spring not to use default resource Location but use what we instruct it.Like below

@Bean
    public WebMvcConfigurerAdapter webMvcConfigurerAdapter()
    {
        return new WebMvcConfigurerAdapter()
        {
            @Override
            public void addResourceHandlers(ResourceHandlerRegistry registry)
            {
                if (!registry.hasMappingForPattern("/**"))
                {
                    // if this is executed spring won't add default resource
                    // locations - add them to the staticContentLocations if
                    // you want to keep them
                    // default locations:
                    // WebMvcAutoConfiguration.RESOURCE_LOCATIONS
                    registry.addResourceHandler("/**").addResourceLocations(
                            staticContentLocations);
                }
            }
        };
    }

If C:/myprj/static has index.html , then http://localhost:portno/index.html should work. Hope that helps.

There are 2 things to consider (Spring Boot v1.5.2.RELEASE)-

1) Check all Controller classes for @EnableWebMvc annotation, remove it if there is any

2) Check the Controller classes for which annotation is used - @RestController or @Controller.

Do not mix Rest API and MVC behaviour in one class. For MVC use @Controller and for REST API use @RestController

Doing above 2 things resolved my issue. Now my spring boot is loading static resources with out any issues.
@Controller => load index.html => loads static files.

@Controller
public class WelcomeController {

    // inject via application.properties
    @Value("${welcome.message:Hello}")
    private String message = "Hello World";

    @RequestMapping("/welcome")
    public String welcome(Map<String, Object> model) {
        model.put("message", this.message);
        return "welcome";
    }

    @RequestMapping("/")
    public String home(Map<String, Object> model) {
        model.put("message", this.message);
        return "index";
    }

}

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Hello</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />


    <link rel="stylesheet/less" th:href="@{/webapp/assets/theme.siberia.less}"/>

    <!-- The app's logic -->
    <script type="text/javascript" data-main="/webapp/app" th:src="@{/webapp/libs/require.js}"></script>
    <script type="text/javascript">
        require.config({
            paths: { text:"/webapp/libs/text" }
        });
    </script>

     <!-- Development only -->
     <script type="text/javascript" th:src="@{/webapp/libs/less.min.js}"></script>


</head>
<body>

</body>
</html>

I had to add thymeleaf dependency to pom.xml. Without this dependency Spring boot didn't find static resources.

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

Spring Boot will use WebMvcAutoConfiguration to config static contents, this class will only take effects when you don't have a custom WebMvcConfigurationSupport bean ,and in my cases , i do have one.

@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class,
        WebMvcConfigurerAdapter.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
        ValidationAutoConfiguration.class })
    public class WebMvcAutoConfiguration {

}

so i'll just have to config it by myself, here is my solution:

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

I've a assets folder in static dir and permitting in SpringConfig which extended WebSecurityConfigurerAdapter.

http.authorizeRequests().antMatchers("/", "/login", "/assets/**")
            .permitAll()

I just stumbled about this 404 topic when I try to create a sandbox example as template (https://github.com/fluentcodes/sandbox/tree/java-spring-boot) from my existing working solution: No static content index.html is provided.

Since the example was rather simple and the existing solution worked I don't get deep into the deep digging spring solutions mentioned.

I looked in the created target classes and there was no static/index.html as expected.

The reason was rather simple: I created the resources not under src/main/ but under src/. Need some time to find since there are so many complex solutions mentioned. For me it had a rather trivial reason.

On the other hand when starting spring boot: Why I don't get any hint, that no static content from location1, location2 .... locationx could be found. Think its a topic.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top