[java] How can I serve static html from spring boot?

I ran the spring-boot-sample-web-static project from here, made this alteration to the pom

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

And added this class to serve a duplicate page index2.html from the same static folder location:

import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class Rester {

    @RequestMapping(value = "/rand", produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    private RandomObj jsonEndpoint() {
        return new RandomObj();
    }

    @RequestMapping(value = "/tw")
    public String somePg() {
        return "index2";
    }
}

The json url works fine, but when I try to access localhost:8080/tw I get a blank page, and this error in the console:

2017-02-22 15:37:22.076 ERROR 21494 --- [nio-8080-exec-9] o.s.boot.web.support.ErrorPageFilter     : Cannot forward to error page for request [/tw] as the response has already been committed. As a result, the response may have the wrong status code. If your application is running on WebSphere Application Server you may be able to resolve this problem by setting com.ibm.ws.webcontainer.invokeFlushAfterService to false

Am I doing something wrong?

This question is related to java spring-boot

The answer is


As it is written before, some folders (/META-INF/resources/, /resources/, /static/, /public/) serve static content by default, conroller misconfiguration can break this behaviour.

It is a common pitfall that people define the base url of a controller in the @RestController annotation, instead of the @RequestMapping annotation on the top of the controllers.

This is wrong:

@RestController("/api/base")
public class MyController {

    @PostMapping
    public String myPostMethod( ...) {

The above example will prevent you from opening the index.html. The Spring expects a POST method at the root, because the myPostMethod is mapped to the "/" path.

You have to use this instead:

@RestController
@RequestMapping("/api/base")
public class MyController {

    @PostMapping
    public String myPostMethod( ...) {

I am using :: Spring Boot :: (v2.0.4.RELEASE) with Spring Framework 5

Spring Boot 2.0 requires Java 8 as a minimum version. Many existing APIs have been updated to take advantage of Java 8 features such as: default methods on interfaces, functional callbacks, and new APIs such as javax.time.

Static Content

By default, Spring Boot serves static content from a directory called /static (or /public or /resources or /META-INF/resources) in the classpath or from the root of the ServletContext. It uses the ResourceHttpRequestHandler from Spring MVC so that you can modify that behavior by adding your own WebMvcConfigurer and overriding the addResourceHandlers method.

By default, resources are mapped on /** and located on /static directory. But you can customize the static loactions programmatically inside our web context configuration class.

@Configuration @EnableWebMvc
public class Static_ResourceHandler implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // When overriding default behavior, you need to add default(/) as well as added static paths(/webapp).

        // src/main/resources/static/...
        registry
            //.addResourceHandler("/**") // « /css/myStatic.css
            .addResourceHandler("/static/**") // « /static/css/myStatic.css
            .addResourceLocations("classpath:/static/") // Default Static Loaction
            .setCachePeriod( 3600 )
            .resourceChain(true) // 4.1
            .addResolver(new GzipResourceResolver()) // 4.1
            .addResolver(new PathResourceResolver()); //4.1

        // src/main/resources/templates/static/...
        registry
            .addResourceHandler("/templates/**") // « /templates/style.css
            .addResourceLocations("classpath:/templates/static/");

        // Do not use the src/main/webapp/... directory if your application is packaged as a jar.
        registry
            .addResourceHandler("/webapp/**") // « /webapp/css/style.css
            .addResourceLocations("/");

        // File located on disk
        registry
            .addResourceHandler("/system/files/**")
            .addResourceLocations("file:///D:/");
    }
}
http://localhost:8080/handlerPath/resource-path+name
                    /static         /css/myStatic.css
                    /webapp         /css/style.css
                    /templates      /style.css

In Spring every request will go through the DispatcherServlet. To avoid Static file request through DispatcherServlet(Front contoller) we configure MVC Static content.

As @STEEL said static resources should not go through Controller. Thymleaf is a ViewResolver which takes the view name form controller and adds prefix and suffix to View Layer.


In Spring boot, /META-INF/resources/, /resources/, static/ and public/ directories are available to serve static contents.

So you can create a static/ or public/ directory under resources/ directory and put your static contents there. And they will be accessible by: http://localhost:8080/your-file.ext. (assuming the server.port is 8080)

You can customize these directories using spring.resources.static-locations in the application.properties.

For example:

spring.resources.static-locations=classpath:/custom/

Now you can use custom/ folder under resources/ to serve static files.

Update:

This is also possible using java config:

@Configuration
public class StaticConfig implements WebMvcConfigurer {

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

This confugration maps contents of custom directory to the http://localhost:8080/static/** url.


You can quickly serve static content in JAVA Spring-boot App via thymeleaf (ref: source)

I assume you have already added Spring Boot plugin apply plugin: 'org.springframework.boot' and the necessary buildscript

Then go ahead and ADD thymeleaf to your build.gradle ==>

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile("org.springframework.boot:spring-boot-starter-thymeleaf")
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

Lets assume you have added home.html at src/main/resources To serve this file, you will need to create a controller.

package com.ajinkya.th.controller;

  import org.springframework.stereotype.Controller;
  import org.springframework.web.bind.annotation.RequestMapping;

  @Controller
  public class HomePageController {

      @RequestMapping("/")
      public String homePage() {
        return "home";
      }

  }

Thats it ! Now restart your gradle server. ./gradlew bootRun


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>