Skip to content

Commit a287e67

Browse files
committed
Introduce 'useRegisteredExtensionsOnly' property in classes using MediaTypeFactory
This commit introduces a `useRegisteredExtensionsOnly` property that indicates whether classes that use the `MediaTypeFactory` for supplying default media types can do so. - In classes that were introduced in Spring 5.0, the `useRegisteredExtensionsOnly` property takes the place of the `useJaf` property that was removed in 0aaa652 - In classes that existed before Spring 5.0, the `useRegisteredExtensionsOnly` property is added in addition to the deprecated `useJaf`, the latter delegating to the former, but with flipped behavior. Issue: SPR-14908
1 parent e2aa880 commit a287e67

File tree

8 files changed

+101
-22
lines changed

8 files changed

+101
-22
lines changed

spring-web/src/main/java/org/springframework/http/MediaTypeFactory.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,9 @@ private static MultiValueMap<String, MediaType> parseMimeTypes() {
104104
* @return the corresponding media type, or {@code null} if none found
105105
*/
106106
public static Optional<MediaType> getMediaType(Resource resource) {
107-
if (resource == null) {
108-
return Optional.empty();
109-
}
110-
String filename = resource.getFilename();
111-
return (filename != null ? getMediaType(filename) : Optional.empty());
107+
return Optional.ofNullable(resource)
108+
.map(Resource::getFilename)
109+
.flatMap(MediaTypeFactory::getMediaType);
112110
}
113111

114112
/**
@@ -117,8 +115,7 @@ public static Optional<MediaType> getMediaType(Resource resource) {
117115
* @return the corresponding media type, or {@code null} if none found
118116
*/
119117
public static Optional<MediaType> getMediaType(String filename) {
120-
List<MediaType> mediaTypes = getMediaTypes(filename);
121-
return (!mediaTypes.isEmpty() ? Optional.of(mediaTypes.get(0)) : Optional.empty());
118+
return getMediaTypes(filename).stream().findFirst();
122119
}
123120

124121
/**

spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ public List<String> resolveFileExtensions(MediaType mediaType) {
143143
* <p>At startup this method returns extensions explicitly registered with
144144
* either {@link PathExtensionContentNegotiationStrategy} or
145145
* {@link ParameterContentNegotiationStrategy}. At runtime if there is a
146-
* "path extension" strategy, the list of extensions may
146+
* "path extension" strategy and its
147+
* {@link PathExtensionContentNegotiationStrategy#setUseRegisteredExtensionsOnly(boolean)
148+
* useRegisteredExtensionsOnly} property is set to "false", the list of extensions may
147149
* increase as file extensions are resolved via
148150
* {@link org.springframework.http.MediaTypeFactory} and cached.
149151
*/

spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ public class ContentNegotiationManagerFactoryBean
100100

101101
private boolean ignoreUnknownPathExtensions = true;
102102

103+
private Boolean useRegisteredExtensionsOnly;
104+
103105
private String parameterName = "format";
104106

105107
private ContentNegotiationStrategy defaultNegotiationStrategy;
@@ -175,10 +177,27 @@ public void setIgnoreUnknownPathExtensions(boolean ignore) {
175177
}
176178

177179
/**
178-
* @deprecated as 5.0, in favor of {@link MediaTypeFactory}, which has no JAF dependency.
180+
* @deprecated as of 5.0, in favor of {@link #setUseRegisteredExtensionsOnly(boolean)}, which
181+
* has reverse behavior.
179182
*/
180183
@Deprecated
181184
public void setUseJaf(boolean useJaf) {
185+
setUseRegisteredExtensionsOnly(!useJaf);
186+
}
187+
188+
/**
189+
* When {@link #setFavorPathExtension favorPathExtension} is set, this
190+
* property determines whether to use only registered {@code MediaType} mappings
191+
* to resolve a path extension to a specific MediaType.
192+
* <p>By default this is not set in which case
193+
* {@code PathExtensionContentNegotiationStrategy} will use defaults if available.
194+
*/
195+
public void setUseRegisteredExtensionsOnly(boolean useRegisteredExtensionsOnly) {
196+
this.useRegisteredExtensionsOnly = useRegisteredExtensionsOnly;
197+
}
198+
199+
private boolean useRegisteredExtensionsOnly() {
200+
return (this.useRegisteredExtensionsOnly != null && this.useRegisteredExtensionsOnly);
182201
}
183202

184203
/**
@@ -244,14 +263,17 @@ public void afterPropertiesSet() {
244263

245264
if (this.favorPathExtension) {
246265
PathExtensionContentNegotiationStrategy strategy;
247-
if (this.servletContext != null) {
266+
if (this.servletContext != null && !useRegisteredExtensionsOnly()) {
248267
strategy = new ServletPathExtensionContentNegotiationStrategy(
249268
this.servletContext, this.mediaTypes);
250269
}
251270
else {
252271
strategy = new PathExtensionContentNegotiationStrategy(this.mediaTypes);
253272
}
254273
strategy.setIgnoreUnknownExtensions(this.ignoreUnknownPathExtensions);
274+
if (this.useRegisteredExtensionsOnly != null) {
275+
strategy.setUseRegisteredExtensionsOnly(this.useRegisteredExtensionsOnly);
276+
}
255277
strategies.add(strategy);
256278
}
257279

spring-web/src/main/java/org/springframework/web/accept/PathExtensionContentNegotiationStrategy.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ public class PathExtensionContentNegotiationStrategy extends AbstractMappingCont
5151

5252
private UrlPathHelper urlPathHelper = new UrlPathHelper();
5353

54+
private boolean useRegisteredExtensionsOnly = false;
55+
5456
private boolean ignoreUnknownExtensions = true;
5557

5658

@@ -81,10 +83,20 @@ public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
8183
}
8284

8385
/**
84-
* @deprecated as 5.0, in favor of {@link MediaTypeFactory}, which has no JAF dependency.
86+
* @deprecated as of 5.0, in favor of {@link #setUseRegisteredExtensionsOnly(boolean)}.
8587
*/
8688
@Deprecated
8789
public void setUseJaf(boolean useJaf) {
90+
setUseRegisteredExtensionsOnly(!useJaf);
91+
}
92+
93+
/**
94+
* Whether to only use the registered mappings to look up file extensions, or also refer to
95+
* defaults.
96+
* <p>By default this is set to {@code false}, meaning that defaults are used.
97+
*/
98+
public void setUseRegisteredExtensionsOnly(boolean useRegisteredExtensionsOnly) {
99+
this.useRegisteredExtensionsOnly = useRegisteredExtensionsOnly;
88100
}
89101

90102
/**
@@ -113,9 +125,11 @@ protected String getMediaTypeKey(NativeWebRequest webRequest) {
113125
protected MediaType handleNoMatch(NativeWebRequest webRequest, String extension)
114126
throws HttpMediaTypeNotAcceptableException {
115127

116-
Optional<MediaType> mediaType = MediaTypeFactory.getMediaType("file." + extension);
117-
if (mediaType.isPresent()) {
118-
return mediaType.get();
128+
if (!this.useRegisteredExtensionsOnly) {
129+
Optional<MediaType> mediaType = MediaTypeFactory.getMediaType("file." + extension);
130+
if (mediaType.isPresent()) {
131+
return mediaType.get();
132+
}
119133
}
120134
if (this.ignoreUnknownExtensions) {
121135
return null;

spring-web/src/main/java/org/springframework/web/accept/ServletPathExtensionContentNegotiationStrategy.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ protected MediaType handleNoMatch(NativeWebRequest webRequest, String extension)
9393
* @return the MediaType for the extension or {@code null}.
9494
* @since 4.3
9595
*/
96+
@Override
9697
public MediaType getMediaTypeForResource(Resource resource) {
9798
MediaType mediaType = null;
9899
if (this.servletContext != null) {

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

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
*/
4343
public class PathExtensionContentTypeResolver extends AbstractMappingContentTypeResolver {
4444

45+
private boolean useRegisteredExtensionsOnly = false;
46+
4547
private boolean ignoreUnknownExtensions = true;
4648

4749

@@ -61,6 +63,15 @@ public PathExtensionContentTypeResolver() {
6163
}
6264

6365

66+
/**
67+
* Whether to only use the registered mappings to look up file extensions, or also refer to
68+
* defaults.
69+
* <p>By default this is set to {@code false}, meaning that defaults are used.
70+
*/
71+
public void setUseRegisteredExtensionsOnly(boolean useRegisteredExtensionsOnly) {
72+
this.useRegisteredExtensionsOnly = useRegisteredExtensionsOnly;
73+
}
74+
6475
/**
6576
* Whether to ignore requests with unknown file extension. Setting this to
6677
* {@code false} results in {@code HttpMediaTypeNotAcceptableException}.
@@ -80,14 +91,16 @@ protected String extractKey(ServerWebExchange exchange) {
8091

8192
@Override
8293
protected MediaType handleNoMatch(String key) throws NotAcceptableStatusException {
83-
Optional<MediaType> mediaType = MediaTypeFactory.getMediaType("file." + key);
84-
if (mediaType.isPresent()) {
85-
return mediaType.get();
94+
if (!this.useRegisteredExtensionsOnly) {
95+
Optional<MediaType> mediaType = MediaTypeFactory.getMediaType("file." + key);
96+
if (mediaType.isPresent()) {
97+
return mediaType.get();
98+
}
8699
}
87-
if (!this.ignoreUnknownExtensions) {
88-
throw new NotAcceptableStatusException(getAllMediaTypes());
100+
if (this.ignoreUnknownExtensions) {
101+
return null;
89102
}
90-
return null;
103+
throw new NotAcceptableStatusException(getAllMediaTypes());
91104
}
92105

93106
/**

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ public class RequestedContentTypeResolverBuilder {
9393

9494
private boolean ignoreUnknownPathExtensions = true;
9595

96+
private Boolean useRegisteredExtensionsOnly;
97+
9698
private String parameterName = "format";
9799

98100
private RequestedContentTypeResolver contentTypeResolver;
@@ -152,6 +154,18 @@ public RequestedContentTypeResolverBuilder ignoreUnknownPathExtensions(boolean i
152154
return this;
153155
}
154156

157+
/**
158+
* When {@link #favorPathExtension favorPathExtension} is set, this
159+
* property determines whether to use only registered {@code MediaType} mappings
160+
* to resolve a path extension to a specific MediaType.
161+
* <p>By default this is not set in which case
162+
* {@code PathExtensionContentNegotiationStrategy} will use defaults if available.
163+
*/
164+
public RequestedContentTypeResolverBuilder useRegisteredExtensionsOnly(boolean useRegisteredExtensionsOnly) {
165+
this.useRegisteredExtensionsOnly = useRegisteredExtensionsOnly;
166+
return this;
167+
}
168+
155169
/**
156170
* Whether a request parameter ("format" by default) should be used to
157171
* determine the requested media type. For this option to work you must
@@ -211,6 +225,9 @@ public CompositeContentTypeResolver build() {
211225
if (this.favorPathExtension) {
212226
PathExtensionContentTypeResolver resolver = new PathExtensionContentTypeResolver(this.mediaTypes);
213227
resolver.setIgnoreUnknownExtensions(this.ignoreUnknownPathExtensions);
228+
if (this.useRegisteredExtensionsOnly != null) {
229+
resolver.setUseRegisteredExtensionsOnly(this.useRegisteredExtensionsOnly);
230+
}
214231
resolvers.add(resolver);
215232
}
216233

spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public ContentNegotiationConfigurer favorPathExtension(boolean favorPathExtensio
120120
* attack protection).
121121
* <p>The path extension strategy will also try to use
122122
* {@link ServletContext#getMimeType} and {@link MediaTypeFactory} to resolve path
123-
* extensions.
123+
* extensions. To change this behavior see the {@link #useRegisteredExtensionsOnly} property.
124124
* @param extension the key to look up
125125
* @param mediaType the media type
126126
* @see #mediaTypes(Map)
@@ -166,10 +166,23 @@ public ContentNegotiationConfigurer ignoreUnknownPathExtensions(boolean ignore)
166166
}
167167

168168
/**
169-
* @deprecated as 5.0, in favor of {@link MediaTypeFactory}, which has no JAF dependency.
169+
* @deprecated as of 5.0, in favor of {@link #useRegisteredExtensionsOnly(boolean)}, which
170+
* has reverse behavior.
170171
*/
171172
@Deprecated
172173
public ContentNegotiationConfigurer useJaf(boolean useJaf) {
174+
return this.useRegisteredExtensionsOnly(!useJaf);
175+
}
176+
177+
/**
178+
* When {@link #favorPathExtension favorPathExtension} is set, this
179+
* property determines whether to use only registered {@code MediaType} mappings
180+
* to resolve a path extension to a specific MediaType.
181+
* <p>By default this is not set in which case
182+
* {@code PathExtensionContentNegotiationStrategy} will use defaults if available.
183+
*/
184+
public ContentNegotiationConfigurer useRegisteredExtensionsOnly(boolean useRegisteredExtensionsOnly) {
185+
this.factory.setUseRegisteredExtensionsOnly(useRegisteredExtensionsOnly);
173186
return this;
174187
}
175188

0 commit comments

Comments
 (0)