diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WelcomePageHandlerMapping.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WelcomePageHandlerMapping.java index 8c2e7a91b1..95351d56b6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WelcomePageHandlerMapping.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WelcomePageHandlerMapping.java @@ -48,14 +48,15 @@ final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping { WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders, ApplicationContext applicationContext, Optional welcomePage, String staticPathPattern) { - if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) { + if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) { + logger.info("Adding welcome page: " + welcomePage.get()); + setRootViewName("forward:index.html"); + } + else if (welcomeTemplateExists(templateAvailabilityProviders, + applicationContext)) { logger.info("Adding welcome page template: index"); setRootViewName("index"); } - else if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) { - logger.info("Adding welcome page: " + welcomePage); - setRootViewName("forward:index.html"); - } } private boolean welcomeTemplateExists( diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WelcomePageHandlerMappingTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WelcomePageHandlerMappingTests.java index dff3a01ba4..89d93b071e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WelcomePageHandlerMappingTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WelcomePageHandlerMappingTests.java @@ -16,14 +16,21 @@ package org.springframework.boot.autoconfigure.web.servlet; +import java.util.Collections; +import java.util.Map; import java.util.Optional; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.junit.Test; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider; +import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @@ -32,10 +39,15 @@ import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.view.AbstractView; +import org.springframework.web.servlet.view.InternalResourceView; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -109,17 +121,55 @@ public class WelcomePageHandlerMappingTests { .andExpect(status().isNotFound())); } + @Test + public void handlesRequestForTemplateThatAcceptsTextHtml() { + this.contextRunner.withUserConfiguration(TemplateConfiguration.class) + .run((context) -> { + MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); + mockMvc.perform(get("/").accept(MediaType.TEXT_HTML)) + .andExpect(status().isOk()) + .andExpect(content().string("index template")); + }); + } + + @Test + public void handlesRequestForTemplateThatAcceptsAll() { + this.contextRunner.withUserConfiguration(TemplateConfiguration.class) + .run((context) -> { + MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); + mockMvc.perform(get("/").accept(MediaType.ALL)) + .andExpect(status().isOk()) + .andExpect(content().string("index template")); + }); + } + + @Test + public void prefersAStaticResourceToATemplate() { + this.contextRunner.withUserConfiguration(StaticResourceConfiguration.class, + TemplateConfiguration.class).run((context) -> { + MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); + mockMvc.perform(get("/").accept(MediaType.ALL)) + .andExpect(status().isOk()) + .andExpect(forwardedUrl("index.html")); + }); + } + @Configuration static class HandlerMappingConfiguration { @Bean public WelcomePageHandlerMapping handlerMapping( ApplicationContext applicationContext, + ObjectProvider templateAvailabilityProviders, ObjectProvider staticIndexPage, @Value("${static-path-pattern:/**}") String staticPathPattern) { return new WelcomePageHandlerMapping( + templateAvailabilityProviders.getIfAvailable( + () -> new TemplateAvailabilityProviders(applicationContext)), + applicationContext, Optional.ofNullable(staticIndexPage.getIfAvailable()), staticPathPattern); + } } @@ -134,4 +184,43 @@ public class WelcomePageHandlerMappingTests { } + @Configuration + static class TemplateConfiguration { + + @Bean + public TemplateAvailabilityProviders templateAvailabilityProviders() { + return new TestTemplateAvailabilityProviders((view, environment, classLoader, + resourceLoader) -> view.equals("index")); + } + + @Bean + public ViewResolver viewResolver() { + return (name, locale) -> { + if (name.startsWith("forward:")) { + return new InternalResourceView(name.substring("forward:".length())); + } + return new AbstractView() { + + @Override + protected void renderMergedOutputModel(Map model, + HttpServletRequest request, HttpServletResponse response) + throws Exception { + response.getWriter().print(name + " template"); + } + + }; + }; + } + + } + + private static class TestTemplateAvailabilityProviders + extends TemplateAvailabilityProviders { + + TestTemplateAvailabilityProviders(TemplateAvailabilityProvider provider) { + super(Collections.singletonList(provider)); + } + + } + } diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index ed1abec578..0f4ed564f7 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -1952,9 +1952,7 @@ By default, resources are mapped on `+/**+`, but you can tune that with the You can also customize the static resource locations by using the `spring.resources.static-locations` property (replacing the default values with a list of directory locations). The root Servlet context path `"/"` is automatically added as a -location as well. If you do this, the default welcome page detection switches to your -custom locations. So, if there is an `index.html` in any of your locations on startup, it -is the home page of the application. +location as well. In addition to the '`standard`' static resource locations mentioned earlier, a special case is made for http://www.webjars.org/[Webjars content]. Any resources with a path in @@ -2022,6 +2020,13 @@ post] and in Spring Framework's {spring-reference}web.html#mvc-config-static-resources[reference documentation]. ==== +[[boot-features-spring-mvc-welcome-page]] +==== Welcome Page +Spring Boot support both static and templated welcome pages. It first looks for an +`index.html` file in the configured static content locations. If one is not found, it +then looks for an `index` template. If either is found it is automatically used as the +welcome page of the application. + [[boot-features-spring-mvc-favicon]]