Spring Boot project with static content generates 404 when running jar
Asked Answered
P

8

32

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.

Pantelegraph answered 26/1, 2014 at 1:14 Comment(0)
P
48

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.

Pantelegraph answered 26/1, 2014 at 1:14 Comment(4)
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.Ungenerous
do you have an example project somewhere so i can see...i still can't get it to workHilary
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>Granoff
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 #24763157Budge
A
8

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')
}
Aerophyte answered 18/3, 2014 at 5:42 Comment(4)
Gradle uses "src/main/resources" by default as well. What's wrong with using that?Ungenerous
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.Aerophyte
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.Ungenerous
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)Twopiece
R
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>
Rexferd answered 1/8, 2017 at 15:30 Comment(2)
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.Pantelegraph
SUPERB !!!! I had trouble getting my index.html at work. I removed a @EnableWebMvc and it worked !!! THANX SO MUCH!Bonefish
M
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.

Madelyn answered 29/1, 2016 at 13:29 Comment(1)
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.Pantelegraph
C
3

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/");
}
Cicelycicenia answered 29/8, 2018 at 2:42 Comment(0)
E
3

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>
Earwig answered 1/10, 2020 at 7:16 Comment(1)
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.Joseph
M
0

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

http.authorizeRequests().antMatchers("/", "/login", "/assets/**")
            .permitAll()
Monoculture answered 18/10, 2018 at 11:18 Comment(0)
G
0

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.

Gold answered 3/9, 2020 at 17:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.