29

I've got static folder with following structure:

index.html
docs/index.html

Spring Boot correctly maps requests / to index.html. But it doesn't map /docs/ request to /docs/index.html (/docs/index.html request works correctly).

How to map folder/subfolder requests to appropriate index.html files?

1
  • Does the view controller mapping I suggested answer your question? If so, please accept it. Otherwise please clarify your problem and I'll be happy to update my answer. Commented Mar 21, 2016 at 13:29

4 Answers 4

26

You can manually add a view controller mapping to make this work:

@Configuration
public class CustomWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/docs").setViewName("redirect:/docs/");
        registry.addViewController("/docs/").setViewName("forward:/docs/index.html");
    super.addViewControllers(registry);
    }
}

The first mapping causes Spring MVC to send a redirect to the client if /docs (without trailing slash) gets requested. This is necessary if you have relative links in /docs/index.html. The second mapping forwards any request to /docs/ internally (without sending a redirect to the client) to the index.html in the docs subdirectory.

Sign up to request clarification or add additional context in comments.

4 Comments

Works with nested subfolders as well: registry.addViewController("/v2/docs").setViewName("redirect:/v2/docs/"); and registry.addViewController("/v2/docs/").setViewName("forward:/v2/docs/index.html");
Great answer, thanks! I've updated it to make it work with Spring 5 / Spring Boot 2
Is there a way to make this work with all subfolders, not just named ones?
Yes, you can add a mapping but let's say you generated a static blog page with Hugo (gohugo.io) and are serving it from the Spring static folder. Hugo has an index.html for every blog entry. So if your blog has 100 pages, you'll need 100 mappings. Also, every time you add a new blog item you'll have to add a mapping for that. Not very convenient.
8

After Java 8 introduced Default Methods in Interfaces, WebMvcConfigurerAdapter has been deprecated in Spring 5 / Spring Boot 2.

Using it will now raise the warning:

The type WebMvcConfigurerAdapter is deprecated

Hence, to make @hzpz's solution work again, we need to change it as follows:

@Configuration
public class CustomWebMvcConfigurer implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/docs").setViewName("redirect:/docs/");
        registry.addViewController("/docs/").setViewName("forward:/docs/index.html");
    }
}

Comments

3

It's not Spring Boot mapping to index.html it's the servlet engine (it's a welcome page). There's only one welcome page (per the spec), and directory browsing is not a feature of the containers.

2 Comments

this explains the problem but doesn't suggests a solution so I can't accept this answer
I don't think there is a solution other than you writing your own directory browser servlet handler. I can suggest that if you like?
3

This one will always try to match requestPath + /index.html if static resource is not found:

import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.PathResourceResolver;
import org.springframework.web.servlet.resource.ResourceResolverChain;

@Configuration
public class ResourceHandlerConfig implements WebMvcConfigurer {

  static class IndexFallbackResourceResolver extends PathResourceResolver{
    @Override
    protected Resource resolveResourceInternal(HttpServletRequest request, String requestPath,
        List<? extends Resource> locations, ResourceResolverChain chain) {
      Resource resource = super.resolveResourceInternal(request, requestPath, locations, chain);
      if(resource==null){
        //try with /index.html
        resource = super.resolveResourceInternal(request, requestPath + "/index.html", locations, chain);
      }
      return resource;
    }
  }

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
        .setOrder(Ordered.LOWEST_PRECEDENCE)
        .addResourceHandler("/**")
        .addResourceLocations("classpath:/static/")
        //first time resolved, that route will always be used from cache
        .resourceChain(true)
        .addResolver(new IndexFallbackResourceResolver());
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.