Spring Boot project with static content generates 404 when running jar

41,270

Solution 1

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.

Solution 2

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')
}

Solution 3

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.

Solution 4

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>

Solution 5

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>
Share:
41,270
Robert Hunt
Author by

Robert Hunt

Updated on October 01, 2020

Comments

  • Robert Hunt
    Robert Hunt almost 4 years

    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.

  • Dave Syer
    Dave Syer over 10 years
    Just for the record: I think you misread the blog because it doesn't mention maven at all, and isn't using a jar archive. If you do exactly as Roy did in the blog it would work.
  • Dave Syer
    Dave Syer over 10 years
    Gradle uses "src/main/resources" by default as well. What's wrong with using that?
  • kgreenek
    kgreenek over 10 years
    That is true, putting it in the resources directly will get your files copied into the jar. However, in order for Spring to recognize and serve your files as static content, you need to package it under a directory called "static", "public", "resources", or "/META-INF/resources/" in the jar file. If you simply put your files in the resources directory, they all get copied to the root directory in the jar and Spring serves 404's when you try to access them.
  • Dave Syer
    Dave Syer over 10 years
    Right, so the simplest (least configuration, maximum functionality) solution is to put your static resources in "src/main/resourecs/static". That's all I meant.
  • Chris DaMour
    Chris DaMour about 10 years
    do you have an example project somewhere so i can see...i still can't get it to work
  • hellboy
    hellboy over 8 years
    can you please edit the answer to remove the "/" in front of the directory path? it should be <directory>src/main/resources</directory> and <directory>static</directory>
  • Robert Hunt
    Robert Hunt over 8 years
    There is an easier way to do this, you can simply specify your location as the value of the spring.resources.static-locations property inside your application.properties / application.yml file or even pass it as a command line argument.
  • Busata
    Busata over 7 years
    If you let the npm/frontend build task depend on the jar however, it will have already collected the /resources/static into the /build/lib blabla, so you need to let it depend on compileJava, so that your static stuff is already build (for future visitors, just had the same issue)
  • ug_
    ug_ about 7 years
    This wasn't working for me, adding a resource in the pom.xml wasn't enough. Instead I had to configure the Spring boot plugin with <addResources>true</addResources>. See this SO answer stackoverflow.com/questions/24762657/…
  • Robert Hunt
    Robert Hunt almost 7 years
    I think your problem may have been due to using @RestController - this annotation is identical to @Controlller but also adds @ResponseBody to every method which would mean anything you return from your controller methods ends up in the response body, possibly transformed by a converter. In your case it would probably mean you see "welcome" or "index" being returned in the browser as a String rather than it loading "welcome.html" or "index.html" views.
  • tm1701
    tm1701 almost 7 years
    SUPERB !!!! I had trouble getting my index.html at work. I removed a @EnableWebMvc and it worked !!! THANX SO MUCH!
  • Cody Pritchard
    Cody Pritchard over 3 years
    Thank you. I was trying to host static content from Javadoc and JaCoCo. I got it work locally, but once I deployed, the links were giving 404 response. I could not figure out why, but adding this dependency made it work.