10

I am using Spring Boot, and am trying to make my static resources (CSS, JS, Fonts) available when deployed. The source code is available for you to look at or clone from https://github.com/joecracko/StaticResourceError.

Right now my CSS, JS, and Font files are not visible to my deployed website.

Here is my project directory structure:

Here is my project directory structure

Here is the root directory of the compiled JAR: I assure you the files are present in their respective folders.

enter image description here

Here is the network error I am seeing:

enter image description here

And here are my sources provided by chrome tools. Note that bar.css appears empty here. You may look at my source code to see that it is not empty.

enter image description here

Here is my homepage.html

<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Insert title here</title>

<!-- Main Styles -->
<link rel="stylesheet" href="/css/bar.css" />
<script src="/js/foo.js"></script>

</head>
<body>
    <div>Welcome to Foo!</div>
</body>
</html>

Here is my Web App Initializer (FooWebAppInitializer.java)

public class FooWebAppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) {

        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.register(ServletConfig.class);

        // Manage the lifecycle of the root application context
        container.addListener(new ContextLoaderListener(rootContext));

        //Spring Security
        container.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
            .addMappingForUrlPatterns(null, false, "/*");

        // Register and map the dispatcher servlet
        ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcherServlet", new DispatcherServlet(rootContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/*");
        dispatcher.addMapping("*.css");
        dispatcher.addMapping("*.eot");
        dispatcher.addMapping("*.svg");
        dispatcher.addMapping("*.ttf");
        dispatcher.addMapping("*.woff");
        dispatcher.addMapping("*.map");
        dispatcher.addMapping("*.js");
        dispatcher.addMapping("*.ico");
    }
}

Here is my Servlet Configuration (ServletConfig.java)

@Configuration
@EnableWebMvc
@ComponentScan({"com.foo"})
public class ServletConfig extends WebMvcAutoConfiguration{

    @Bean
    MultipartResolver multipartResolver() {
        return new StandardServletMultipartResolver();
    }

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

And for kicks, My Spring Security Config (WebSecurityConfig.java)

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .anyRequest().permitAll();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**"); // #3
    }
}

2 Answers2

19

Put static resources under the directory:

/src/main/resources/static

You can also use public or resources instead of static as folder name.

Explanation: your build tool (Maven or Gradle) will copy all the content from /src/main/resources/ in the application classpath and, as written in Spring Boot's docs, all the content from a directory called /static (or /public or /resources) in the classpath will be served as static content.


This directory could works also, but it is discouraged:

/src/main/webapp/

Do not use the src/main/webapp directory if your application will be packaged as a jar. Although this directory is a common standard, it will only work with war packaging and it will be silently ignored by most build tools if you generate a jar.

Andrea
  • 15,900
  • 18
  • 65
  • 84
  • The Spring Boot documentation says that's not the best idea for a JAR build. http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-spring-mvc-static-content – John Douglas George Mar 20 '15 at 17:57
  • You nailed it! Putting my static files in src/main/resources/static/ worked like a charm! And this is how I ended up referencing them from my html. Notice that there is no 'static' in front of them. Bonus points if you can tell me WHY this works. – John Douglas George Mar 20 '15 at 20:12
  • Both these answers are bad. You will likely run into problems with live refresh. Better to follow the spring boot docs and include resources under /static (off of your root project folder - not in your src/main/resources folder). Then you'll be able to make changes in chrome, save them in your chrome workspace and see the updates live with no restarts. In addition, if you're deploying as a jar, maven wont pick up resources in a /static folder, so to fix that you'll want to read /questions/27566967/no-mapping-found-for-http-request-with-uri-web-inf-pages-mainpage-jsp-in-disp/27567879#27567879 – John Deverall Dec 11 '15 at 22:53
  • @JohnDeverall Thanks for pointing out possible problems. I can well understand the problem with live refresh and [here there are some solutions](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-hotswapping.html#howto-reload-static-content). The other problem you pointed out seems related to put content in `/src/main/webapp` and packaging the application as a jar, but this folder is discouraged in the answer. So, staying on `/src/main/resources/static`, are there other problems we could have other than live refresh? – Andrea Dec 12 '15 at 09:25
  • @Andrea the other issue I mentioned is to do with maven's convention over configuration approach. /static does not follow a maven convention, so anything in /static will not be automatically included in build artifacts. You'll need to configure maven, as in the other question, but with static instead of src/main/webapp. If you opt for /src/main/resources/static instead of just static, I'm not aware of any other issues you'll run into besides live refresh of course – John Deverall Dec 12 '15 at 22:43
  • As soon as I add @EnableWebMvc to my config, spring stops resolving the resources in the static folder. – pedrocgsousa Dec 16 '16 at 10:48
  • @JohnDeverall This will work if the webapp Jar artifact is generated by spring-boot-maven-plugin – V.Vidyasagar May 12 '17 at 13:54
  • I was using intellij and had a problem accessing the static contents from the url. I had to manually build the project using maven for the url to work for the css file. – Sameer Khanal Apr 05 '18 at 04:56
3

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("/")
    public String home(Map<String, Object> model) {
        model.put("message", this.message);
        return "index";
    }

}

index.html

<!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>
gjp
  • 111
  • 4
  • In my case everything was overall fine, but requests without any subpaths (i. e. GET http://localhost:8080) would return 404 instead of the index.html file. Removing @EnableWebMvc annotation fixed it for me. – Iorweth333 Oct 06 '22 at 08:56
  • Thank you! It helps me to resolve issue with using custom json/yml file instead of generated one. One controller has @EnableWebMvc and it blocks loading swagger-ui – jhenya-d Dec 10 '22 at 15:23