Skip to content

Commit cb60473

Browse files
committed
ResourceWebHandler uses MediaTypeFactory directly
There is no need for ResourceWebHandler to go through the PathExtensionContentTypeResolver when MediaTypeFactory makes it easy to perform such lookups for a given Resource. This does not support any extensions explicitly registered through a WebFluxConfigurer but it would be easy enough to pass those into ResourceWebHandler as a simple Map<String, MediaType>, should the need arise. Issue: SPR-15639
1 parent b65bfdb commit cb60473

File tree

5 files changed

+4
-135
lines changed

5 files changed

+4
-135
lines changed

spring-webflux/src/main/java/org/springframework/web/reactive/accept/PathExtensionContentTypeResolver.java

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,9 @@
2020
import java.util.Map;
2121
import java.util.Optional;
2222

23-
import org.springframework.core.io.Resource;
2423
import org.springframework.http.MediaType;
2524
import org.springframework.http.MediaTypeFactory;
2625
import org.springframework.lang.Nullable;
27-
import org.springframework.util.Assert;
2826
import org.springframework.util.StringUtils;
2927
import org.springframework.web.server.NotAcceptableStatusException;
3028
import org.springframework.web.server.ServerWebExchange;
@@ -104,26 +102,4 @@ protected MediaType handleNoMatch(String key) throws NotAcceptableStatusExceptio
104102
throw new NotAcceptableStatusException(getAllMediaTypes());
105103
}
106104

107-
/**
108-
* A public method exposing the knowledge of the path extension resolver to
109-
* determine the media type for a given {@link Resource}. First it checks
110-
* the explicitly registered mappings and then falls back on {@link MediaTypeFactory}.
111-
* @param resource the resource
112-
* @return the MediaType for the extension, or {@code null} if none determined
113-
*/
114-
@Nullable
115-
public MediaType resolveMediaTypeForResource(Resource resource) {
116-
Assert.notNull(resource, "Resource must not be null");
117-
MediaType mediaType = null;
118-
String filename = resource.getFilename();
119-
String extension = StringUtils.getFilenameExtension(filename);
120-
if (extension != null) {
121-
mediaType = getMediaType(extension);
122-
}
123-
if (mediaType == null) {
124-
mediaType = MediaTypeFactory.getMediaType(filename).orElse(null);
125-
}
126-
return mediaType;
127-
}
128-
129105
}

spring-webflux/src/main/java/org/springframework/web/reactive/config/ResourceHandlerRegistry.java

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.springframework.context.ApplicationContext;
2727
import org.springframework.lang.Nullable;
2828
import org.springframework.util.Assert;
29-
import org.springframework.web.reactive.accept.CompositeContentTypeResolver;
3029
import org.springframework.web.reactive.handler.AbstractHandlerMapping;
3130
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
3231
import org.springframework.web.reactive.resource.ResourceWebHandler;
@@ -56,8 +55,6 @@ public class ResourceHandlerRegistry {
5655

5756
private final ApplicationContext applicationContext;
5857

59-
private final CompositeContentTypeResolver contentTypeResolver;
60-
6158
private final List<ResourceHandlerRegistration> registrations = new ArrayList<>();
6259

6360
private int order = Integer.MAX_VALUE -1;
@@ -68,20 +65,9 @@ public class ResourceHandlerRegistry {
6865
* @param applicationContext the Spring application context
6966
*/
7067
public ResourceHandlerRegistry(ApplicationContext applicationContext) {
71-
this(applicationContext, null);
72-
}
73-
74-
/**
75-
* Create a new resource handler registry for the given application context.
76-
* @param applicationContext the Spring application context
77-
* @param contentTypeResolver the content type resolver to use
78-
*/
79-
public ResourceHandlerRegistry(ApplicationContext applicationContext,
80-
@Nullable CompositeContentTypeResolver contentTypeResolver) {
8168

8269
Assert.notNull(applicationContext, "ApplicationContext is required");
8370
this.applicationContext = applicationContext;
84-
this.contentTypeResolver = contentTypeResolver;
8571
}
8672

8773

@@ -133,23 +119,19 @@ protected AbstractHandlerMapping getHandlerMapping() {
133119
if (this.registrations.isEmpty()) {
134120
return null;
135121
}
136-
137122
Map<String, WebHandler> urlMap = new LinkedHashMap<>();
138123
for (ResourceHandlerRegistration registration : this.registrations) {
139124
for (String pathPattern : registration.getPathPatterns()) {
140125
ResourceWebHandler handler = registration.getRequestHandler();
141-
handler.setContentTypeResolver(this.contentTypeResolver);
142126
try {
143127
handler.afterPropertiesSet();
144-
handler.afterSingletonsInstantiated();
145128
}
146129
catch (Exception ex) {
147130
throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", ex);
148131
}
149132
urlMap.put(pathPattern, handler);
150133
}
151134
}
152-
153135
SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
154136
handlerMapping.setOrder(this.order);
155137
handlerMapping.setUrlMap(urlMap);

spring-webflux/src/main/java/org/springframework/web/reactive/config/WebFluxConfigurationSupport.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,7 @@ protected RouterFunctionMapping createRouterFunctionMapping() {
235235
*/
236236
@Bean
237237
public HandlerMapping resourceHandlerMapping() {
238-
ResourceHandlerRegistry registry =
239-
new ResourceHandlerRegistry(this.applicationContext, webFluxContentTypeResolver());
238+
ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext);
240239
addResourceHandlers(registry);
241240

242241
AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();

spring-webflux/src/main/java/org/springframework/web/reactive/resource/ResourceWebHandler.java

Lines changed: 3 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@
2323
import java.util.ArrayList;
2424
import java.util.Collections;
2525
import java.util.EnumSet;
26-
import java.util.HashMap;
2726
import java.util.List;
28-
import java.util.Map;
2927
import java.util.Optional;
3028
import java.util.Set;
3129

@@ -35,14 +33,14 @@
3533
import reactor.core.publisher.Mono;
3634

3735
import org.springframework.beans.factory.InitializingBean;
38-
import org.springframework.beans.factory.SmartInitializingSingleton;
3936
import org.springframework.core.ResolvableType;
4037
import org.springframework.core.io.Resource;
4138
import org.springframework.http.CacheControl;
4239
import org.springframework.http.HttpHeaders;
4340
import org.springframework.http.HttpMethod;
4441
import org.springframework.http.HttpStatus;
4542
import org.springframework.http.MediaType;
43+
import org.springframework.http.MediaTypeFactory;
4644
import org.springframework.http.codec.ResourceHttpMessageWriter;
4745
import org.springframework.lang.Nullable;
4846
import org.springframework.util.Assert;
@@ -51,8 +49,6 @@
5149
import org.springframework.util.ResourceUtils;
5250
import org.springframework.util.StringUtils;
5351
import org.springframework.web.reactive.HandlerMapping;
54-
import org.springframework.web.reactive.accept.CompositeContentTypeResolver;
55-
import org.springframework.web.reactive.accept.PathExtensionContentTypeResolver;
5652
import org.springframework.web.server.MethodNotAllowedException;
5753
import org.springframework.web.server.ServerWebExchange;
5854
import org.springframework.web.server.WebHandler;
@@ -85,7 +81,7 @@
8581
* @author Brian Clozel
8682
* @since 5.0
8783
*/
88-
public class ResourceWebHandler implements WebHandler, InitializingBean, SmartInitializingSingleton {
84+
public class ResourceWebHandler implements WebHandler, InitializingBean {
8985

9086
/** Set of supported HTTP methods */
9187
private static final Set<HttpMethod> SUPPORTED_METHODS = EnumSet.of(HttpMethod.GET, HttpMethod.HEAD);
@@ -103,10 +99,6 @@ public class ResourceWebHandler implements WebHandler, InitializingBean, SmartIn
10399

104100
private ResourceHttpMessageWriter resourceHttpMessageWriter;
105101

106-
private CompositeContentTypeResolver contentTypeResolver;
107-
108-
private PathExtensionContentTypeResolver pathExtensionResolver;
109-
110102

111103
/**
112104
* Set the {@code List} of {@code Resource} paths to use as sources
@@ -197,23 +189,6 @@ public ResourceHttpMessageWriter getResourceHttpMessageWriter() {
197189
return this.resourceHttpMessageWriter;
198190
}
199191

200-
/**
201-
* Configure a {@link CompositeContentTypeResolver} to help determine the
202-
* media types for resources being served. If the manager contains a path
203-
* extension resolver it will be checked for registered file extension.
204-
* @param contentTypeResolver the resolver in use
205-
*/
206-
public void setContentTypeResolver(CompositeContentTypeResolver contentTypeResolver) {
207-
this.contentTypeResolver = contentTypeResolver;
208-
}
209-
210-
/**
211-
* Return the configured {@link CompositeContentTypeResolver}.
212-
*/
213-
@Nullable
214-
public CompositeContentTypeResolver getContentTypeResolver() {
215-
return this.contentTypeResolver;
216-
}
217192

218193
@Override
219194
public void afterPropertiesSet() throws Exception {
@@ -250,23 +225,6 @@ protected void initAllowedLocations() {
250225
}
251226
}
252227

253-
@Override
254-
public void afterSingletonsInstantiated() {
255-
this.pathExtensionResolver = initContentNegotiationStrategy();
256-
}
257-
258-
protected PathExtensionContentTypeResolver initContentNegotiationStrategy() {
259-
Map<String, MediaType> mediaTypes = null;
260-
if (getContentTypeResolver() != null) {
261-
PathExtensionContentTypeResolver strategy =
262-
getContentTypeResolver().findResolver(PathExtensionContentTypeResolver.class);
263-
if (strategy != null) {
264-
mediaTypes = new HashMap<>(strategy.getMediaTypes());
265-
}
266-
}
267-
return new PathExtensionContentTypeResolver(mediaTypes);
268-
}
269-
270228

271229
/**
272230
* Processes a resource request.
@@ -317,7 +275,7 @@ public Mono<Void> handle(ServerWebExchange exchange) {
317275
}
318276

319277
// Check the media type for the resource
320-
MediaType mediaType = getMediaType(exchange, resource);
278+
MediaType mediaType = MediaTypeFactory.getMediaType(resource).orElse(null);
321279
if (mediaType != null) {
322280
if (logger.isTraceEnabled()) {
323281
logger.trace("Determined media type '" + mediaType + "' for " + resource);
@@ -474,20 +432,6 @@ private ResourceTransformerChain createTransformerChain(ResourceResolverChain re
474432
return new DefaultResourceTransformerChain(resolverChain, getResourceTransformers());
475433
}
476434

477-
/**
478-
* Determine the media type for the given request and the resource matched
479-
* to it. This implementation tries to determine the MediaType based on the
480-
* file extension of the Resource via
481-
* {@link PathExtensionContentTypeResolver#resolveMediaTypeForResource(Resource)}.
482-
* @param exchange the current exchange
483-
* @param resource the resource to check
484-
* @return the corresponding media type, or {@code null} if none found
485-
*/
486-
@Nullable
487-
protected MediaType getMediaType(ServerWebExchange exchange, Resource resource) {
488-
return this.pathExtensionResolver.resolveMediaTypeForResource(resource);
489-
}
490-
491435
/**
492436
* Set headers on the response. Called for both GET and HEAD requests.
493437
* @param exchange current exchange

spring-webflux/src/test/java/org/springframework/web/reactive/resource/ResourceWebHandlerTests.java

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@
4949
import org.springframework.mock.http.server.reactive.test.MockServerWebExchange;
5050
import org.springframework.util.StringUtils;
5151
import org.springframework.web.reactive.HandlerMapping;
52-
import org.springframework.web.reactive.accept.CompositeContentTypeResolver;
53-
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
5452
import org.springframework.web.server.MethodNotAllowedException;
5553
import org.springframework.web.server.ServerWebExchange;
5654

@@ -86,7 +84,6 @@ public void setup() throws Exception {
8684
this.handler.setLocations(paths);
8785
this.handler.setCacheControl(CacheControl.maxAge(3600, TimeUnit.SECONDS));
8886
this.handler.afterPropertiesSet();
89-
this.handler.afterSingletonsInstantiated();
9087
}
9188

9289

@@ -159,7 +156,6 @@ public void getVersionedResource() throws Exception {
159156
versionResolver.addFixedVersionStrategy("versionString", "/**");
160157
this.handler.setResourceResolvers(Arrays.asList(versionResolver, new PathResourceResolver()));
161158
this.handler.afterPropertiesSet();
162-
this.handler.afterSingletonsInstantiated();
163159

164160
MockServerWebExchange exchange = MockServerHttpRequest.get("").toExchange();
165161
setPathWithinHandlerMapping(exchange, "versionString/foo.css");
@@ -222,39 +218,12 @@ public void getResourceFromSubDirectoryOfAlternatePath() throws Exception {
222218
assertResponseBody(exchange, "function foo() { console.log(\"hello world\"); }");
223219
}
224220

225-
@Test // SPR-13658
226-
public void getResourceWithRegisteredMediaType() throws Exception {
227-
CompositeContentTypeResolver contentTypeResolver = new RequestedContentTypeResolverBuilder()
228-
.mediaType("css", new MediaType("foo", "bar"))
229-
.build();
230-
231-
List<Resource> paths = Collections.singletonList(new ClassPathResource("test/", getClass()));
232-
ResourceWebHandler handler = new ResourceWebHandler();
233-
handler.setLocations(paths);
234-
handler.setContentTypeResolver(contentTypeResolver);
235-
handler.afterPropertiesSet();
236-
handler.afterSingletonsInstantiated();
237-
238-
MockServerWebExchange exchange = MockServerHttpRequest.get("").toExchange();
239-
setPathWithinHandlerMapping(exchange, "foo.css");
240-
handler.handle(exchange).block(TIMEOUT);
241-
242-
assertEquals(MediaType.parseMediaType("foo/bar"), exchange.getResponse().getHeaders().getContentType());
243-
assertResponseBody(exchange, "h1 { color:red; }");
244-
}
245-
246221
@Test // SPR-14577
247222
public void getMediaTypeWithFavorPathExtensionOff() throws Exception {
248-
CompositeContentTypeResolver contentTypeResolver = new RequestedContentTypeResolverBuilder()
249-
.favorPathExtension(false)
250-
.build();
251-
252223
List<Resource> paths = Collections.singletonList(new ClassPathResource("test/", getClass()));
253224
ResourceWebHandler handler = new ResourceWebHandler();
254225
handler.setLocations(paths);
255-
handler.setContentTypeResolver(contentTypeResolver);
256226
handler.afterPropertiesSet();
257-
handler.afterSingletonsInstantiated();
258227

259228
MockServerWebExchange exchange = MockServerHttpRequest.get("")
260229
.header("Accept", "application/json,text/plain,*/*").toExchange();
@@ -367,7 +336,6 @@ public void initAllowedLocationsWithExplicitConfiguration() throws Exception {
367336
handler.setResourceResolvers(Collections.singletonList(pathResolver));
368337
handler.setLocations(Arrays.asList(location1, location2));
369338
handler.afterPropertiesSet();
370-
handler.afterSingletonsInstantiated();
371339

372340
Resource[] locations = pathResolver.getAllowedLocations();
373341
assertEquals(1, locations.length);

0 commit comments

Comments
 (0)