diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistration.java index 07d5312c9fca..7ced7193441b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistration.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistration.java @@ -223,11 +223,19 @@ protected String[] getPathPatterns() { return this.pathPatterns; } + /** + * Create the resource handler. + * Return a {@link ResourceHttpRequestHandler} instance. + */ + protected ResourceHttpRequestHandler createRequestHandler() { + return new ResourceHttpRequestHandler(); + } + /** * Return a {@link ResourceHttpRequestHandler} instance. */ protected ResourceHttpRequestHandler getRequestHandler() { - ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler(); + ResourceHttpRequestHandler handler = createRequestHandler(); if (this.resourceChainRegistration != null) { handler.setResourceResolvers(this.resourceChainRegistration.getResourceResolvers()); handler.setResourceTransformers(this.resourceChainRegistration.getResourceTransformers()); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java index a0f74e8094e6..9c9145f3aada 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistry.java @@ -130,6 +130,22 @@ public ResourceHandlerRegistration addResourceHandler(String... pathPatterns) { return registration; } + /** + * Add a resource handler to serve static resources. The handler is invoked + * for requests that match one of the specified URL path patterns. + *
Patterns such as {@code "/static/**"} or {@code "/css/{filename:\\w+\\.css}"} + * are supported. This method allows users to register a subclass of {@link ResourceHandlerRegistration}. + *
For pattern syntax see {@link PathPattern} when parsed patterns + * are {@link PathMatchConfigurer#setPatternParser enabled} or + * {@link AntPathMatcher} otherwise. The syntax is largely the same with + * {@link PathPattern} more tailored for web usage and more efficient. + */ + public ResourceHandlerRegistration addResourceHandler(ResourceHandlerRegistration registration) { + Assert.notNull(registration, "ResourceHandlerRegistration must not be null"); + this.registrations.add(registration); + return registration; + } + /** * Whether a resource handler has already been registered for the given path pattern. */ diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java index 322683fdefdc..d13ff3d52538 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/config/annotation/ResourceHandlerRegistryTests.java @@ -19,11 +19,16 @@ import java.nio.charset.StandardCharsets; import java.util.List; +import jakarta.servlet.http.HttpServletRequest; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.cache.concurrent.ConcurrentMapCache; +import org.springframework.core.io.Resource; import org.springframework.http.CacheControl; +import org.springframework.http.MediaType; +import org.springframework.util.StringUtils; import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.servlet.HandlerMapping; @@ -71,6 +76,8 @@ void setup() { this.registration = this.registry.addResourceHandler("/resources/**"); this.registration.addResourceLocations("classpath:org/springframework/web/servlet/config/annotation/"); + this.registry.addResourceHandler(new NoExtensionAsHtmlResourceHttpHandlerRegistration("/noext/**")) + .addResourceLocations("classpath:org/springframework/web/servlet/config/annotation/noext/"); this.response = new MockHttpServletResponse(); } @@ -224,4 +231,45 @@ void lastModifiedDisabled() { assertThat(handler.isUseLastModified()).isFalse(); } + @Test + void noExtensionResource() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setMethod("GET"); + request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "/noExtResource"); + + ResourceHttpRequestHandler handler = getHandler("/noext/**"); + handler.handleRequest(request, this.response); + + assertThat(this.response.getContentType()).isEqualTo("text/html"); + } + + + static class NoExtensionAsHtmlResourceHttpHandlerRegistration extends ResourceHandlerRegistration { + + public NoExtensionAsHtmlResourceHttpHandlerRegistration(String... pathPatterns) { + super(pathPatterns); + } + + @Override + protected ResourceHttpRequestHandler createRequestHandler() { + return new NoExtensionAsHtmlResourceHttpHandler(); + } + } + + static class NoExtensionAsHtmlResourceHttpHandler extends ResourceHttpRequestHandler { + + @Override + protected @Nullable MediaType getMediaType(HttpServletRequest request, Resource resource) { + String filename = resource.getFilename(); + String ext = StringUtils.getFilenameExtension(filename); + if (!StringUtils.hasText(ext)) { + return MediaType.TEXT_HTML; + } + else { + return super.getMediaType(request, resource); + } + } + } + + } diff --git a/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/annotation/noext/noExtResource b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/annotation/noext/noExtResource new file mode 100644 index 000000000000..ea613204e612 --- /dev/null +++ b/spring-webmvc/src/test/resources/org/springframework/web/servlet/config/annotation/noext/noExtResource @@ -0,0 +1 @@ +no ext \ No newline at end of file