From 94f42d11e5dc41a5be5cf43cc3881391f3e0c311 Mon Sep 17 00:00:00 2001 From: Vedran Pavic Date: Fri, 15 Jul 2022 14:13:28 +0200 Subject: [PATCH] Add support for customizing WebJars resource handler path pattern At present, both Spring MVC and Spring WebFlux auto-configurations hardcode the path pattern for WebJars resource handlers to "/webjars/**", which means users are unable to change the path. This commit introduces "spring.mvc.webjars-path-pattern" and "spring.webflux.webjars-path-pattern" configuration properties that allow customization of WebJars resource handler path pattern. See gh-31769 --- .../web/reactive/WebFluxAutoConfiguration.java | 5 +++-- .../web/reactive/WebFluxProperties.java | 14 ++++++++++++++ .../web/servlet/WebMvcAutoConfiguration.java | 3 ++- .../web/servlet/WebMvcProperties.java | 16 +++++++++++++++- .../reactive/WebFluxAutoConfigurationTests.java | 13 +++++++++++++ .../servlet/WebMvcAutoConfigurationTests.java | 12 ++++++++++++ .../src/docs/asciidoc/web/reactive.adoc | 3 ++- .../src/docs/asciidoc/web/servlet.adoc | 3 ++- 8 files changed, 63 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java index 7611d5fe14..37ed6a5efb 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfiguration.java @@ -189,8 +189,9 @@ public class WebFluxAutoConfiguration { logger.debug("Default resource handling disabled"); return; } - if (!registry.hasMappingForPattern("/webjars/**")) { - ResourceHandlerRegistration registration = registry.addResourceHandler("/webjars/**") + String webjarsPathPattern = this.webFluxProperties.getWebjarsPathPattern(); + if (!registry.hasMappingForPattern(webjarsPathPattern)) { + ResourceHandlerRegistration registration = registry.addResourceHandler(webjarsPathPattern) .addResourceLocations("classpath:/META-INF/resources/webjars/"); configureResourceCaching(registration); customizeResourceHandlerRegistration(registration); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java index fa2083785c..e7b2b1ba2f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxProperties.java @@ -23,6 +23,7 @@ import org.springframework.util.StringUtils; * {@link ConfigurationProperties properties} for Spring WebFlux. * * @author Brian Clozel + * @author Vedran Pavic * @since 2.0.0 */ @ConfigurationProperties(prefix = "spring.webflux") @@ -40,6 +41,11 @@ public class WebFluxProperties { */ private String staticPathPattern = "/**"; + /** + * Path pattern used for WebJar assets. + */ + private String webjarsPathPattern = "/webjars/**"; + public String getBasePath() { return this.basePath; } @@ -76,6 +82,14 @@ public class WebFluxProperties { this.staticPathPattern = staticPathPattern; } + public String getWebjarsPathPattern() { + return this.webjarsPathPattern; + } + + public void setWebjarsPathPattern(String webjarsPathPattern) { + this.webjarsPathPattern = webjarsPathPattern; + } + public static class Format { /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java index 790ca3d9ba..ded6840f9b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.java @@ -329,7 +329,8 @@ public class WebMvcAutoConfiguration { logger.debug("Default resource handling disabled"); return; } - addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/"); + addResourceHandler(registry, this.mvcProperties.getWebjarsPathPattern(), + "classpath:/META-INF/resources/webjars/"); addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> { registration.addResourceLocations(this.resourceProperties.getStaticLocations()); if (this.servletContext != null) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcProperties.java index 00c5f161a9..92e42caff6 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -35,6 +35,7 @@ import org.springframework.validation.DefaultMessageCodesResolver; * @author Stephane Nicoll * @author EddĂș MelĂ©ndez * @author Brian Clozel + * @author Vedran Pavic * @since 2.0.0 */ @ConfigurationProperties(prefix = "spring.mvc") @@ -91,6 +92,11 @@ public class WebMvcProperties { */ private String staticPathPattern = "/**"; + /** + * Path pattern used for WebJar assets. + */ + private String webjarsPathPattern = "/webjars/**"; + private final Async async = new Async(); private final Servlet servlet = new Servlet(); @@ -188,6 +194,14 @@ public class WebMvcProperties { this.staticPathPattern = staticPathPattern; } + public String getWebjarsPathPattern() { + return this.webjarsPathPattern; + } + + public void setWebjarsPathPattern(String webjarsPathPattern) { + this.webjarsPathPattern = webjarsPathPattern; + } + public Async getAsync() { return this.async; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java index 3b19c57177..bb9cd8889b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/WebFluxAutoConfigurationTests.java @@ -109,6 +109,7 @@ import static org.mockito.Mockito.mock; * @author Brian Clozel * @author Andy Wilkinson * @author Artsiom Yudovin + * @author Vedran Pavic */ class WebFluxAutoConfigurationTests { @@ -186,6 +187,18 @@ class WebFluxAutoConfigurationTests { }); } + @Test + void shouldMapWebjarsToCustomPath() { + this.contextRunner.withPropertyValues("spring.webflux.webjars-path-pattern:/assets/**").run((context) -> { + SimpleUrlHandlerMapping hm = context.getBean("resourceHandlerMapping", SimpleUrlHandlerMapping.class); + assertThat(hm.getUrlMap().get("/assets/**")).isInstanceOf(ResourceWebHandler.class); + ResourceWebHandler webjarsHandler = (ResourceWebHandler) hm.getUrlMap().get("/assets/**"); + assertThat(webjarsHandler.getLocations()).hasSize(1); + assertThat(webjarsHandler.getLocations().get(0)) + .isEqualTo(new ClassPathResource("/META-INF/resources/webjars/")); + }); + } + @Test void shouldNotMapResourcesWhenDisabled() { this.contextRunner.withPropertyValues("spring.web.resources.add-mappings:false") diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java index 83c88cad76..855423d97a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfigurationTests.java @@ -146,6 +146,7 @@ import static org.mockito.Mockito.mock; * @author Kristine Jetzke * @author Artsiom Yudovin * @author Scott Frederick + * @author Vedran Pavic */ class WebMvcAutoConfigurationTests { @@ -195,6 +196,17 @@ class WebMvcAutoConfigurationTests { }); } + @Test + void customWebjarsHandlerMapping() { + this.contextRunner.withPropertyValues("spring.mvc.webjars-path-pattern:/assets/**").run((context) -> { + Map> locations = getResourceMappingLocations(context); + assertThat(locations.get("/assets/**")).hasSize(1); + assertThat(locations.get("/assets/**").get(0)) + .isEqualTo(new ClassPathResource("/META-INF/resources/webjars/")); + assertThat(getResourceResolvers(context, "/assets/**")).hasSize(1); + }); + } + @Test void resourceHandlerMappingOverrideWebjars() { this.contextRunner.withUserConfiguration(WebJars.class).run((context) -> { diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc index d70b7694c1..f47aaf1515 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/reactive.adoc @@ -84,7 +84,8 @@ If you do so, the default welcome page detection switches to your custom locatio So, if there is an `index.html` in any of your locations on startup, it is the home page of the application. In addition to the "`standard`" static resource locations listed earlier, a special case is made for https://www.webjars.org/[Webjars content]. -Any resources with a path in `+/webjars/**+` are served from jar files if they are packaged in the Webjars format. +By default, any resources with a path in `+/webjars/**+` are served from jar files if they are packaged in the Webjars format. +The path can be customized with the configprop:spring.webflux.webjars-path-pattern[] property. TIP: Spring WebFlux applications do not strictly depend on the servlet API, so they cannot be deployed as war files and do not use the `src/main/webapp` directory. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/servlet.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/servlet.adoc index a0015a4f4b..56f0e866fd 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/servlet.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/servlet.adoc @@ -107,7 +107,8 @@ You can also customize the static resource locations by using the configprop:spr The root servlet context path, `"/"`, is automatically added as a location as well. In addition to the "`standard`" static resource locations mentioned earlier, a special case is made for https://www.webjars.org/[Webjars content]. -Any resources with a path in `+/webjars/**+` are served from jar files if they are packaged in the Webjars format. +By default, any resources with a path in `+/webjars/**+` are served from jar files if they are packaged in the Webjars format. +The path can be customized with the configprop:spring.mvc.webjars-path-pattern[] property. TIP: Do not use the `src/main/webapp` directory if your application is packaged as a jar. Although this directory is a common standard, it works *only* with war packaging, and it is silently ignored by most build tools if you generate a jar.