From 2a5eab4b899dcb2000655039b6b754715348d66f Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Wed, 15 Mar 2023 10:16:27 +0100 Subject: [PATCH] Ignore quality factor when filtering out "*/*" Prior to this commit, the `RequestedContentTypeResolverBuilder` would create a `RequestedContentTypeResolver` that internally delegates to a list of resolvers. Each resolver would either return the list of requested media types, or a singleton list with the "*/*" media type; in this case this signals that the resolver cannot find a specific media type requested and that we should continue with the next resolver in the list. Media Types returned by resolvers can contain parameters, such as the quality factor. If the HTTP client requests "*/*;q=0.8", the `HeaderContentTypeResolver` will return this as a singleton list. While this has been resolved from the request, such a media type should not be selected over other media types that could be returned by other resolvers. This commit changes the `RequestedContentTypeResolverBuilder` so that it does not select "*/*;q=0.8" as the requested media type, but instead continues delegating to other resolvers in the list. This means we need to remove the quality factor before comparing it to the "*/*" for equality check. Fixes gh-29915 --- .../RequestedContentTypeResolverBuilder.java | 7 ++++- ...uestedContentTypeResolverBuilderTests.java | 30 ++++++++++++------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/RequestedContentTypeResolverBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/RequestedContentTypeResolverBuilder.java index e94f25a2d7..c85a021351 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/RequestedContentTypeResolverBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/RequestedContentTypeResolverBuilder.java @@ -93,7 +93,7 @@ public class RequestedContentTypeResolverBuilder { return exchange -> { for (RequestedContentTypeResolver resolver : resolvers) { List mediaTypes = resolver.resolveMediaTypes(exchange); - if (mediaTypes.equals(RequestedContentTypeResolver.MEDIA_TYPE_ALL_LIST)) { + if (isMediaTypeAll(mediaTypes)) { continue; } return mediaTypes; @@ -102,6 +102,11 @@ public class RequestedContentTypeResolverBuilder { }; } + private boolean isMediaTypeAll(List mediaTypes) { + return mediaTypes.size() == 1 + && mediaTypes.get(0).removeQualityValue().equals(MediaType.ALL); + } + /** * Helper to create and configure {@link ParameterContentTypeResolver}. diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/accept/RequestedContentTypeResolverBuilderTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/accept/RequestedContentTypeResolverBuilderTests.java index f4b7dc36fa..f0dccf848b 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/accept/RequestedContentTypeResolverBuilderTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/accept/RequestedContentTypeResolverBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,8 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class RequestedContentTypeResolverBuilderTests { @Test - public void defaultSettings() throws Exception { - + void defaultSettings() { RequestedContentTypeResolver resolver = new RequestedContentTypeResolverBuilder().build(); MockServerWebExchange exchange = MockServerWebExchange.from( MockServerHttpRequest.get("/flower").accept(MediaType.IMAGE_GIF)); @@ -45,8 +44,7 @@ public class RequestedContentTypeResolverBuilderTests { } @Test - public void parameterResolver() throws Exception { - + void parameterResolver() { RequestedContentTypeResolverBuilder builder = new RequestedContentTypeResolverBuilder(); builder.parameterResolver().mediaType("json", MediaType.APPLICATION_JSON); RequestedContentTypeResolver resolver = builder.build(); @@ -58,8 +56,7 @@ public class RequestedContentTypeResolverBuilderTests { } @Test - public void parameterResolverWithCustomParamName() throws Exception { - + void parameterResolverWithCustomParamName() { RequestedContentTypeResolverBuilder builder = new RequestedContentTypeResolverBuilder(); builder.parameterResolver().mediaType("json", MediaType.APPLICATION_JSON).parameterName("s"); RequestedContentTypeResolver resolver = builder.build(); @@ -71,8 +68,7 @@ public class RequestedContentTypeResolverBuilderTests { } @Test // SPR-10513 - public void fixedResolver() throws Exception { - + void fixedResolver() { RequestedContentTypeResolverBuilder builder = new RequestedContentTypeResolverBuilder(); builder.fixedResolver(MediaType.APPLICATION_JSON); RequestedContentTypeResolver resolver = builder.build(); @@ -84,8 +80,7 @@ public class RequestedContentTypeResolverBuilderTests { } @Test // SPR-12286 - public void resolver() throws Exception { - + void resolver() { RequestedContentTypeResolverBuilder builder = new RequestedContentTypeResolverBuilder(); builder.resolver(new FixedContentTypeResolver(MediaType.APPLICATION_JSON)); RequestedContentTypeResolver resolver = builder.build(); @@ -99,4 +94,17 @@ public class RequestedContentTypeResolverBuilderTests { assertThat(mediaTypes).isEqualTo(Collections.singletonList(MediaType.APPLICATION_JSON)); } + @Test + void removeQualityFactorForMediaTypeAllChecks() { + RequestedContentTypeResolverBuilder builder = new RequestedContentTypeResolverBuilder(); + builder.resolver(new HeaderContentTypeResolver()); + builder.resolver(new FixedContentTypeResolver(MediaType.APPLICATION_JSON)); + RequestedContentTypeResolver resolver = builder.build(); + + MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/") + .accept(MediaType.valueOf("*/*;q=0.8"))); + List mediaTypes = resolver.resolveMediaTypes(exchange); + assertThat(mediaTypes).isEqualTo(Collections.singletonList(MediaType.APPLICATION_JSON)); + } + }