Merge pull request #10545 from Bruce Brouwer

* gh-10545:
  Polish "Auto-configure templated welcome page"
  Auto-configure templated welcome page
pull/11017/merge
Andy Wilkinson 7 years ago
commit 844aa18173

@ -48,6 +48,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidatorAdapter;
import org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain;
@ -57,6 +58,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter;
import org.springframework.boot.web.servlet.filter.OrderedHttpPutFormContentFilter;
import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -130,6 +132,7 @@ import org.springframework.web.servlet.view.InternalResourceViewResolver;
* @author Eddú Meléndez
* @author Stephane Nicoll
* @author Kristine Jetzke
* @author Bruce Brouwer
*/
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ -330,8 +333,11 @@ public class WebMvcAutoConfiguration {
}
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping() {
return new WelcomePageHandlerMapping(getWelcomePage(),
public WelcomePageHandlerMapping welcomePageHandlerMapping(
ApplicationContext applicationContext) {
return new WelcomePageHandlerMapping(
new TemplateAvailabilityProviders(applicationContext),
applicationContext, getWelcomePage(),
this.mvcProperties.getStaticPathPattern());
}

@ -24,6 +24,8 @@ import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.template.TemplateAvailabilityProviders;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@ -37,19 +39,38 @@ import org.springframework.web.servlet.mvc.ParameterizableViewController;
* static page is preferred.
*
* @author Andy Wilkinson
* @author Bruce Brouwer
*/
final class WelcomePageHandlerMapping extends AbstractUrlHandlerMapping {
private static final Log logger = LogFactory.getLog(WelcomePageHandlerMapping.class);
WelcomePageHandlerMapping(Optional<Resource> welcomePage, String staticPathPattern) {
WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,
ApplicationContext applicationContext, Optional<Resource> welcomePage,
String staticPathPattern) {
if (welcomePage.isPresent() && "/**".equals(staticPathPattern)) {
logger.info("Adding welcome page: " + welcomePage.get());
ParameterizableViewController controller = new ParameterizableViewController();
controller.setViewName("forward:index.html");
setRootHandler(controller);
setOrder(0);
setRootViewName("forward:index.html");
}
else if (welcomeTemplateExists(templateAvailabilityProviders,
applicationContext)) {
logger.info("Adding welcome page template: index");
setRootViewName("index");
}
}
private boolean welcomeTemplateExists(
TemplateAvailabilityProviders templateAvailabilityProviders,
ApplicationContext applicationContext) {
return templateAvailabilityProviders.getProvider("index",
applicationContext) != null;
}
private void setRootViewName(final String viewName) {
ParameterizableViewController controller = new ParameterizableViewController();
controller.setViewName(viewName);
setRootHandler(controller);
setOrder(0);
}
@Override

@ -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> templateAvailabilityProviders,
ObjectProvider<Resource> 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<String, Object> 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));
}
}
}

@ -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]]

Loading…
Cancel
Save