@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
/ *
* Copyright 2012 - 2020 the original author or authors .
* Copyright 2012 - 2021 the original author or authors .
*
* Licensed under the Apache License , Version 2 . 0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
@ -17,13 +17,14 @@
@@ -17,13 +17,14 @@
package org.springframework.boot.autoconfigure.web.servlet ;
import java.time.Duration ;
import java.util.Arrays ;
import java.util.HashSet ;
import java.util.List ;
import java.util.ListIterator ;
import java.util.Map ;
import java.util.Optional ;
import java.util.Set ;
import javax.servlet.Servlet ;
import javax.servlet.ServletContext ;
import org.apache.commons.logging.Log ;
import org.apache.commons.logging.LogFactory ;
@ -70,10 +71,10 @@ import org.springframework.core.io.ResourceLoader;
@@ -70,10 +71,10 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.core.task.AsyncTaskExecutor ;
import org.springframework.format.FormatterRegistry ;
import org.springframework.format.support.FormattingConversionService ;
import org.springframework.http.CacheControl ;
import org.springframework.http.MediaType ;
import org.springframework.http.converter.HttpMessageConverter ;
import org.springframework.util.ClassUtils ;
import org.springframework.util.PathMatcher ;
import org.springframework.validation.DefaultMessageCodesResolver ;
import org.springframework.validation.MessageCodesResolver ;
import org.springframework.validation.Validator ;
@ -85,11 +86,13 @@ import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
@@ -85,11 +86,13 @@ import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.context.request.NativeWebRequest ;
import org.springframework.web.context.request.RequestAttributes ;
import org.springframework.web.context.request.RequestContextListener ;
import org.springframework.web.context.support.ServletContextResource ;
import org.springframework.web.filter.FormContentFilter ;
import org.springframework.web.filter.HiddenHttpMethodFilter ;
import org.springframework.web.filter.RequestContextFilter ;
import org.springframework.web.servlet.DispatcherServlet ;
import org.springframework.web.servlet.HandlerExceptionResolver ;
import org.springframework.web.servlet.HandlerMapping ;
import org.springframework.web.servlet.LocaleResolver ;
import org.springframework.web.servlet.View ;
import org.springframework.web.servlet.ViewResolver ;
@ -104,6 +107,7 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
@@ -104,6 +107,7 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport ;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer ;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver ;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping ;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver ;
import org.springframework.web.servlet.i18n.FixedLocaleResolver ;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver ;
@ -111,12 +115,14 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
@@ -111,12 +115,14 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping ;
import org.springframework.web.servlet.resource.AppCacheManifestTransformer ;
import org.springframework.web.servlet.resource.EncodedResourceResolver ;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler ;
import org.springframework.web.servlet.resource.ResourceResolver ;
import org.springframework.web.servlet.resource.ResourceUrlProvider ;
import org.springframework.web.servlet.resource.VersionResourceResolver ;
import org.springframework.web.servlet.view.BeanNameViewResolver ;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver ;
import org.springframework.web.servlet.view.InternalResourceViewResolver ;
import org.springframework.web.util.UrlPathHelper ;
/ * *
* { @link EnableAutoConfiguration Auto - configuration } for { @link EnableWebMvc Web MVC } .
@ -151,7 +157,7 @@ public class WebMvcAutoConfiguration {
@@ -151,7 +157,7 @@ public class WebMvcAutoConfiguration {
* /
public static final String DEFAULT_SUFFIX = "" ;
private static final String [ ] SERVLET_LOCATIONS = { "/" } ;
private static final String SERVLET_LOCATION = "/" ;
@Bean
@ConditionalOnMissingBean ( HiddenHttpMethodFilter . class )
@ -167,13 +173,6 @@ public class WebMvcAutoConfiguration {
@@ -167,13 +173,6 @@ public class WebMvcAutoConfiguration {
return new OrderedFormContentFilter ( ) ;
}
static String [ ] getResourceLocations ( String [ ] staticLocations ) {
String [ ] locations = new String [ staticLocations . length + SERVLET_LOCATIONS . length ] ;
System . arraycopy ( staticLocations , 0 , locations , 0 , staticLocations . length ) ;
System . arraycopy ( SERVLET_LOCATIONS , 0 , locations , staticLocations . length , SERVLET_LOCATIONS . length ) ;
return locations ;
}
// Defined as a nested config to ensure WebMvcConfigurer is not read when not
// on the classpath
@Configuration ( proxyBeanMethods = false )
@ -182,26 +181,17 @@ public class WebMvcAutoConfiguration {
@@ -182,26 +181,17 @@ public class WebMvcAutoConfiguration {
@Order ( 0 )
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
private static final Log logger = LogFactory . getLog ( WebMvcConfigurer . class ) ;
private final ResourceProperties resourceProperties ;
private final WebMvcProperties mvcProperties ;
private final ListableBeanFactory beanFactory ;
private final ObjectProvider < HttpMessageConverters > messageConvertersProvider ;
final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer ;
public WebMvcAutoConfigurationAdapter ( ResourceProperties resourceProperties , WebMvcProperties mvcProperties ,
ListableBeanFactory beanFactory , ObjectProvider < HttpMessageConverters > messageConvertersProvider ,
ObjectProvider < ResourceHandlerRegistrationCustomizer > resourceHandlerRegistrationCustomizerProvider ) {
this . resourceProperties = resourceProperties ;
public WebMvcAutoConfigurationAdapter ( WebMvcProperties mvcProperties , ListableBeanFactory beanFactory ,
ObjectProvider < HttpMessageConverters > messageConvertersProvider ) {
this . mvcProperties = mvcProperties ;
this . beanFactory = beanFactory ;
this . messageConvertersProvider = messageConvertersProvider ;
this . resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider . getIfAvailable ( ) ;
}
@Override
@ -303,37 +293,6 @@ public class WebMvcAutoConfiguration {
@@ -303,37 +293,6 @@ public class WebMvcAutoConfiguration {
ApplicationConversionService . addBeans ( registry , this . beanFactory ) ;
}
@Override
public void addResourceHandlers ( ResourceHandlerRegistry registry ) {
if ( ! this . resourceProperties . isAddMappings ( ) ) {
logger . debug ( "Default resource handling disabled" ) ;
return ;
}
Duration cachePeriod = this . resourceProperties . getCache ( ) . getPeriod ( ) ;
CacheControl cacheControl = this . resourceProperties . getCache ( ) . getCachecontrol ( ) . toHttpCacheControl ( ) ;
if ( ! registry . hasMappingForPattern ( "/webjars/**" ) ) {
customizeResourceHandlerRegistration ( registry . addResourceHandler ( "/webjars/**" )
. addResourceLocations ( "classpath:/META-INF/resources/webjars/" )
. setCachePeriod ( getSeconds ( cachePeriod ) ) . setCacheControl ( cacheControl ) ) ;
}
String staticPathPattern = this . mvcProperties . getStaticPathPattern ( ) ;
if ( ! registry . hasMappingForPattern ( staticPathPattern ) ) {
customizeResourceHandlerRegistration ( registry . addResourceHandler ( staticPathPattern )
. addResourceLocations ( getResourceLocations ( this . resourceProperties . getStaticLocations ( ) ) )
. setCachePeriod ( getSeconds ( cachePeriod ) ) . setCacheControl ( cacheControl ) ) ;
}
}
private Integer getSeconds ( Duration cachePeriod ) {
return ( cachePeriod ! = null ) ? ( int ) cachePeriod . getSeconds ( ) : null ;
}
private void customizeResourceHandlerRegistration ( ResourceHandlerRegistration registration ) {
if ( this . resourceHandlerRegistrationCustomizer ! = null ) {
this . resourceHandlerRegistrationCustomizer . customize ( registration ) ;
}
}
@Bean
@ConditionalOnMissingBean ( { RequestContextListener . class , RequestContextFilter . class } )
@ConditionalOnMissingFilterBean ( RequestContextFilter . class )
@ -349,22 +308,31 @@ public class WebMvcAutoConfiguration {
@@ -349,22 +308,31 @@ public class WebMvcAutoConfiguration {
@Configuration ( proxyBeanMethods = false )
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
private static final Log logger = LogFactory . getLog ( WebMvcConfigurer . class ) ;
private final ResourceProperties resourceProperties ;
private final WebMvcProperties mvcProperties ;
private final ListableBeanFactory beanFactory ;
private final WebMvcRegistrations mvcRegistrations ;
private final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer ;
private ResourceLoader resourceLoader ;
private final ListableBeanFactory beanFactory ;
private final Set < String > autoConfiguredResourceHandlers = new HashSet < > ( ) ;
public EnableWebMvcConfiguration ( ResourceProperties resourceProperties ,
ObjectProvider < WebMvcProperties > mvcPropertiesProvider ,
ObjectProvider < WebMvcRegistrations > mvcRegistrationsProvider , ListableBeanFactory beanFactory ) {
ObjectProvider < WebMvcRegistrations > mvcRegistrationsProvider ,
ObjectProvider < ResourceHandlerRegistrationCustomizer > resourceHandlerRegistrationCustomizerProvider ,
ListableBeanFactory beanFactory ) {
this . resourceProperties = resourceProperties ;
this . mvcProperties = mvcPropertiesProvider . getIfAvailable ( ) ;
this . mvcRegistrations = mvcRegistrationsProvider . getIfUnique ( ) ;
this . resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider . getIfAvailable ( ) ;
this . beanFactory = beanFactory ;
}
@ -401,6 +369,72 @@ public class WebMvcAutoConfiguration {
@@ -401,6 +369,72 @@ public class WebMvcAutoConfiguration {
resourceUrlProvider ) ;
}
@Bean
@Override
public HandlerMapping resourceHandlerMapping ( UrlPathHelper urlPathHelper , PathMatcher pathMatcher ,
ContentNegotiationManager contentNegotiationManager , FormattingConversionService conversionService ,
ResourceUrlProvider resourceUrlProvider ) {
HandlerMapping mapping = super . resourceHandlerMapping ( urlPathHelper , pathMatcher , contentNegotiationManager ,
conversionService , resourceUrlProvider ) ;
if ( mapping instanceof SimpleUrlHandlerMapping ) {
addServletContextResourceHandlerMapping ( ( SimpleUrlHandlerMapping ) mapping ) ;
}
return mapping ;
}
private void addServletContextResourceHandlerMapping ( SimpleUrlHandlerMapping mapping ) {
Map < String , ? > urlMap = mapping . getUrlMap ( ) ;
String pattern = this . mvcProperties . getStaticPathPattern ( ) ;
Object handler = urlMap . get ( pattern ) ;
if ( handler instanceof ResourceHttpRequestHandler
& & this . autoConfiguredResourceHandlers . contains ( pattern ) ) {
addServletContextResourceHandlerMapping ( ( ResourceHttpRequestHandler ) handler ) ;
}
}
private void addServletContextResourceHandlerMapping ( ResourceHttpRequestHandler handler ) {
ServletContext servletContext = getServletContext ( ) ;
if ( servletContext ! = null ) {
List < Resource > locations = handler . getLocations ( ) ;
locations . add ( new ServletContextResource ( servletContext , SERVLET_LOCATION ) ) ;
}
}
@Override
protected void addResourceHandlers ( ResourceHandlerRegistry registry ) {
super . addResourceHandlers ( registry ) ;
if ( ! this . resourceProperties . isAddMappings ( ) ) {
logger . debug ( "Default resource handling disabled" ) ;
return ;
}
addResourceHandler ( registry , "/webjars/**" , "classpath:/META-INF/resources/webjars/" ) ;
addResourceHandler ( registry , this . mvcProperties . getStaticPathPattern ( ) ,
this . resourceProperties . getStaticLocations ( ) ) ;
}
private void addResourceHandler ( ResourceHandlerRegistry registry , String pattern , String . . . locations ) {
if ( registry . hasMappingForPattern ( pattern ) ) {
return ;
}
ResourceHandlerRegistration registration = registry . addResourceHandler ( pattern ) ;
registration . addResourceLocations ( locations ) ;
registration . setCachePeriod ( getSeconds ( this . resourceProperties . getCache ( ) . getPeriod ( ) ) ) ;
registration . setCacheControl ( this . resourceProperties . getCache ( ) . getCachecontrol ( ) . toHttpCacheControl ( ) ) ;
customizeResourceHandlerRegistration ( registration ) ;
this . autoConfiguredResourceHandlers . add ( pattern ) ;
}
private Integer getSeconds ( Duration cachePeriod ) {
return ( cachePeriod ! = null ) ? ( int ) cachePeriod . getSeconds ( ) : null ;
}
private void customizeResourceHandlerRegistration ( ResourceHandlerRegistration registration ) {
if ( this . resourceHandlerRegistrationCustomizer ! = null ) {
this . resourceHandlerRegistrationCustomizer . customize ( registration ) ;
}
}
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping ( ApplicationContext applicationContext ,
FormattingConversionService mvcConversionService , ResourceUrlProvider mvcResourceUrlProvider ) {
@ -412,22 +446,34 @@ public class WebMvcAutoConfiguration {
@@ -412,22 +446,34 @@ public class WebMvcAutoConfiguration {
return welcomePageHandlerMapping ;
}
private Optional < Resource > getWelcomePage ( ) {
String [ ] locations = getResourceLocations ( this . resourceProperties . getStaticLocations ( ) ) ;
return Arrays . stream ( locations ) . map ( this : : getIndexHtml ) . filter ( this : : isReadable ) . findFirst ( ) ;
private Resource getWelcomePage ( ) {
for ( String location : this . resourceProperties . getStaticLocations ( ) ) {
Resource indexHtml = getIndexHtml ( location ) ;
if ( indexHtml ! = null ) {
return indexHtml ;
}
}
ServletContext servletContext = getServletContext ( ) ;
if ( servletContext ! = null ) {
return getIndexHtml ( new ServletContextResource ( servletContext , SERVLET_LOCATION ) ) ;
}
return null ;
}
private Resource getIndexHtml ( String location ) {
return this . resourceLoader . getResource ( location + "index.html" ) ;
return getIndexHtml ( this . resourceLoader . getResource ( location ) ) ;
}
private boolean isReadable ( Resource resource ) {
private Resource getIndexHtml ( Resource location ) {
try {
return resource . exists ( ) & & ( resource . getURL ( ) ! = null ) ;
Resource resource = location . createRelative ( "index.html" ) ;
if ( resource . exists ( ) & & ( resource . getURL ( ) ! = null ) ) {
return resource ;
}
}
catch ( Exception ex ) {
return false ;
}
return null ;
}
@Bean