From e38623df87cf6cbc529724d6cf1170874c05bcf0 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 27 Jun 2016 14:56:55 -0400 Subject: [PATCH] Fix MediaType lookup for ResourceHttpRequestHandler As of 4.3 ResourceHttpRequestHandler delegates to the configured ContentNegotiationManager, or one created internally, to look up the media type for are resource. This commit ensures the internally created ContentNegotiationManager is correctly injected with the ServletContext through which it can perform lookups as before. Also the ServletPathContentNegotiationStrategy now checks the ServletContext first and then delegates to its parent the PathContentNegotiationStrategy and not vice versa. This is consistent with how handleNoMatch (also in the same class) works and also matches how ResourceHttpRequestHandler worked before 4.3. Issue: SPR-14368 --- ...thExtensionContentNegotiationStrategy.java | 11 +++++--- .../resource/ResourceHttpRequestHandler.java | 23 +++++++-------- .../ResourceHttpRequestHandlerTests.java | 28 +++++++++++++++++++ 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java b/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java index 931d6ebe977..10d80b2bee4 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java @@ -92,15 +92,18 @@ public class ServletPathExtensionContentNegotiationStrategy extends PathExtensio * @since 4.3 */ public MediaType getMediaTypeForResource(Resource resource) { - MediaType mediaType = super.getMediaTypeForResource(resource); - if (mediaType == null) { + MediaType mediaType = null; + if (this.servletContext != null) { String mimeType = this.servletContext.getMimeType(resource.getFilename()); if (StringUtils.hasText(mimeType)) { mediaType = MediaType.parseMediaType(mimeType); } } - if (MediaType.APPLICATION_OCTET_STREAM.equals(mediaType)) { - mediaType = null; + if (mediaType == null || MediaType.APPLICATION_OCTET_STREAM.equals(mediaType)) { + MediaType superMediaType = super.getMediaTypeForResource(resource); + if (superMediaType != null) { + mediaType = superMediaType; + } } return mediaType; } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index e338e991d26..64ceea5ee95 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; +import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; @@ -110,6 +111,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator private ContentNegotiationManager contentNegotiationManager; + private final ContentNegotiationManagerFactoryBean cnmFactoryBean = new ContentNegotiationManagerFactoryBean(); + private CorsConfiguration corsConfiguration; @@ -249,6 +252,11 @@ public class ResourceHttpRequestHandler extends WebContentGenerator return this.corsConfiguration; } + @Override + protected void initServletContext(ServletContext servletContext) { + this.cnmFactoryBean.setServletContext(servletContext); + } + @Override public void afterPropertiesSet() throws Exception { @@ -261,7 +269,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator } initAllowedLocations(); if (this.contentNegotiationManager == null) { - this.contentNegotiationManager = initContentNegotiationManager(); + this.cnmFactoryBean.afterPropertiesSet(); + this.contentNegotiationManager = this.cnmFactoryBean.getObject(); } if (this.resourceHttpMessageConverter == null) { this.resourceHttpMessageConverter = new ResourceHttpMessageConverter(); @@ -291,18 +300,6 @@ public class ResourceHttpRequestHandler extends WebContentGenerator } } - /** - * Create the {@code ContentNegotiationManager} to use to resolve the - * {@link MediaType} for requests. This implementation delegates to - * {@link ContentNegotiationManagerFactoryBean} with default settings. - */ - protected ContentNegotiationManager initContentNegotiationManager() { - ContentNegotiationManagerFactoryBean factory = new ContentNegotiationManagerFactoryBean(); - factory.afterPropertiesSet(); - return factory.getObject(); - } - - /** * Processes a resource request. *

Checks for the existence of the requested resource in the configured list of locations. diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java index 52feb87d3f2..c78f75739ef 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java @@ -281,6 +281,34 @@ public class ResourceHttpRequestHandlerTests { assertEquals("h1 { color:red; }", this.response.getContentAsString()); } + @Test // SPR-14368 + public void getResourceWithMediaTypeResolvedThroughServletContext() throws Exception { + MockServletContext servletContext = new MockServletContext() { + + @Override + public String getMimeType(String filePath) { + return "foo/bar"; + } + + @Override + public String getVirtualServerName() { + return null; + } + }; + + List paths = Collections.singletonList(new ClassPathResource("test/", getClass())); + this.handler = new ResourceHttpRequestHandler(); + this.handler.setServletContext(servletContext); + this.handler.setLocations(paths); + this.handler.afterPropertiesSet(); + + this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.css"); + this.handler.handleRequest(this.request, this.response); + + assertEquals("foo/bar", this.response.getContentType()); + assertEquals("h1 { color:red; }", this.response.getContentAsString()); + } + @Test public void invalidPath() throws Exception { for (HttpMethod method : HttpMethod.values()) {