Improve parameter-based content negotiation

Closes gh-36925
This commit is contained in:
rstoyanchev
2026-06-22 10:42:51 +01:00
parent cc0ca1b6a5
commit 5059bbd58e
4 changed files with 23 additions and 10 deletions
@@ -148,9 +148,11 @@ public abstract class AbstractMappingContentNegotiationStrategy extends MappingM
throws HttpMediaTypeNotAcceptableException {
if (!isUseRegisteredExtensionsOnly()) {
Optional<MediaType> mediaType = MediaTypeFactory.getMediaType("file." + key);
if (mediaType.isPresent()) {
return mediaType.get();
if (key.indexOf('.') == -1) {
Optional<MediaType> mediaType = MediaTypeFactory.getMediaType("file." + key);
if (mediaType.isPresent()) {
return mediaType.get();
}
}
}
if (isIgnoreUnknownExtensions()) {
@@ -57,7 +57,7 @@ public class MappingMediaTypeFileExtensionResolver implements MediaTypeFileExten
public MappingMediaTypeFileExtensionResolver(@Nullable Map<String, MediaType> mediaTypes) {
if (mediaTypes != null) {
Set<String> allFileExtensions = CollectionUtils.newHashSet(mediaTypes.size());
mediaTypes.forEach((extension, mediaType) -> addMapping(extension.toLowerCase(Locale.ROOT), mediaType));
mediaTypes.forEach(this::addMapping);
this.allFileExtensions.addAll(allFileExtensions);
}
}
@@ -74,12 +74,12 @@ public class MappingMediaTypeFileExtensionResolver implements MediaTypeFileExten
/**
* Map an extension to a MediaType. Ignore if extension already mapped.
*/
protected void addMapping(String extension, MediaType mediaType) {
MediaType previous = this.mediaTypes.putIfAbsent(extension, mediaType);
protected void addMapping(String key, MediaType mediaType) {
key = key.toLowerCase(Locale.ROOT);
MediaType previous = this.mediaTypes.putIfAbsent(key, mediaType);
if (previous == null) {
this.fileExtensions.computeIfAbsent(
mediaType, key -> new CopyOnWriteArrayList<>()).add(extension);
this.allFileExtensions.add(extension);
this.fileExtensions.computeIfAbsent(mediaType, k -> new CopyOnWriteArrayList<>()).add(key);
this.allFileExtensions.add(key);
}
}
@@ -49,7 +49,7 @@ class MappingContentNegotiationStrategyTests {
}
@Test
void resolveMediaTypesViaMediaTypeFactory() throws Exception {
void resolveMediaTypesViaFactory() throws Exception {
Map<String, MediaType> mapping = null;
TestMappingContentNegotiationStrategy strategy = new TestMappingContentNegotiationStrategy("xml", mapping);
@@ -68,6 +68,15 @@ class MappingContentNegotiationStrategyTests {
.isInstanceOf(HttpMediaTypeNotAcceptableException.class);
}
@Test
void resolveMediaTypesInvalidKey() {
Map<String, MediaType> mapping = null;
TestMappingContentNegotiationStrategy strategy = new TestMappingContentNegotiationStrategy("not.json", mapping);
assertThatThrownBy(() -> strategy.resolveMediaTypes(null))
.isInstanceOf(HttpMediaTypeNotAcceptableException.class);
}
@Test
void resolveMediaTypesNullKey() throws Exception {
Map<String, MediaType> mapping = Collections.singletonMap("json", MediaType.APPLICATION_JSON);
@@ -75,6 +75,8 @@ class MappingMediaTypeFileExtensionResolverTests {
map.put("jSoN", MediaType.APPLICATION_JSON);
MappingMediaTypeFileExtensionResolver resolver = new MappingMediaTypeFileExtensionResolver(map);
map.forEach(resolver::addMapping);
assertThat(resolver.getAllFileExtensions()).containsExactly("json");
}