diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/ApiVersionResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/ApiVersionResolver.java index 77c7baae398..0d468b3357f 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/ApiVersionResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/ApiVersionResolver.java @@ -17,6 +17,7 @@ package org.springframework.web.reactive.accept; import org.jspecify.annotations.Nullable; +import reactor.core.publisher.Mono; import org.springframework.web.server.ServerWebExchange; @@ -24,17 +25,31 @@ import org.springframework.web.server.ServerWebExchange; * Contract to extract the version from a request. * * @author Rossen Stoyanchev + * @author Jonathan Kaplan * @since 7.0 */ @FunctionalInterface -public -interface ApiVersionResolver { +public interface ApiVersionResolver { + + /** + * Resolve the version for the given exchange. + * This method wraps the synchronous {@code resolveVersion} method + * and provides a reactive alternative. + * @param exchange the current exchange + * @return {@code Mono} emitting the version value, or an empty {@code Mono} + * @since 7.0.3 + */ + Mono resolveApiVersion(ServerWebExchange exchange); /** * Resolve the version for the given exchange. * @param exchange the current exchange * @return the version value, or {@code null} if not found + * @deprecated in favor of {@link #resolveApiVersion(ServerWebExchange)} */ - @Nullable String resolveVersion(ServerWebExchange exchange); + @Deprecated(since = "7.0.3", forRemoval = true) + default @Nullable String resolveVersion(ServerWebExchange exchange) { + return null; + } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/ApiVersionStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/ApiVersionStrategy.java index ff8cc2038e6..fc71a6c5257 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/ApiVersionStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/ApiVersionStrategy.java @@ -17,6 +17,7 @@ package org.springframework.web.reactive.accept; import org.jspecify.annotations.Nullable; +import reactor.core.publisher.Mono; import org.springframework.web.accept.InvalidApiVersionException; import org.springframework.web.accept.MissingApiVersionException; @@ -27,17 +28,32 @@ import org.springframework.web.server.ServerWebExchange; * to manage API versioning for an application. * * @author Rossen Stoyanchev + * @author Jonathan Kaplan * @since 7.0 * @see DefaultApiVersionStrategy */ public interface ApiVersionStrategy { + /** + * Resolve the version value from a request. + * @param exchange the current exchange + * @return a {@code Mono} emitting the raw version as a {@code String}, + * or an empty {@code Mono} if no version is found + * @since 7.0.3 + * @see ApiVersionResolver + */ + default Mono resolveApiVersion(ServerWebExchange exchange) { + return Mono.justOrEmpty(resolveVersion(exchange)); + } + /** * Resolve the version value from a request, e.g. from a request header. * @param exchange the current exchange * @return the version, if present or {@code null} * @see ApiVersionResolver + * @deprecated as of 7.0.3, in favor of {@link #resolveApiVersion(ServerWebExchange)} */ + @Deprecated(since = "7.0.3", forRemoval = true) @Nullable String resolveVersion(ServerWebExchange exchange); @@ -64,12 +80,49 @@ public interface ApiVersionStrategy { */ @Nullable Comparable getDefaultVersion(); + /** + * Convenience method to resolve, parse, and validate the request version, + * or return the default version if configured. + * @param exchange the current exchange + * @return a {@code Mono} emitting the request version, a validation error, + * or an empty {@code Mono} if there is no version + * @since 7.0.3 + */ + default Mono> resolveParseAndValidateApiVersion(ServerWebExchange exchange) { + + Mono> result = resolveApiVersion(exchange) + .map(value -> { + Comparable version; + try { + version = parseVersion(value); + } + catch (Exception ex) { + throw new InvalidApiVersionException(value, null, ex); + } + validateVersion(version, exchange); + return version; + }); + + return result.switchIfEmpty(Mono.fromSupplier(() -> { + Comparable defaultVersion = getDefaultVersion(); + if (defaultVersion != null) { + return defaultVersion; + } + else { + validateVersion(null, exchange); + return null; + } + })); + } + /** * Convenience method to return the parsed and validated request version, * or the default version if configured. * @param exchange the current exchange * @return the parsed request version, or the default version + * @deprecated in favor of {@link #resolveParseAndValidateApiVersion(ServerWebExchange)} */ + @Deprecated(since = "7.0.3", forRemoval = true) default @Nullable Comparable resolveParseAndValidateVersion(ServerWebExchange exchange) { String value = resolveVersion(exchange); Comparable version; diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/DefaultApiVersionStrategy.java b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/DefaultApiVersionStrategy.java index da641e2e14b..a6fd704c2e5 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/DefaultApiVersionStrategy.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/DefaultApiVersionStrategy.java @@ -23,6 +23,8 @@ import java.util.TreeSet; import java.util.function.Predicate; import org.jspecify.annotations.Nullable; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import org.springframework.util.Assert; import org.springframework.web.accept.ApiVersionParser; @@ -152,6 +154,7 @@ public class DefaultApiVersionStrategy implements ApiVersionStrategy { } } + @SuppressWarnings("removal") @Override public @Nullable String resolveVersion(ServerWebExchange exchange) { for (ApiVersionResolver resolver : this.versionResolvers) { @@ -163,6 +166,13 @@ public class DefaultApiVersionStrategy implements ApiVersionStrategy { return null; } + @Override + public Mono resolveApiVersion(ServerWebExchange exchange) { + return Flux.fromIterable(this.versionResolvers) + .flatMap(resolver -> resolver.resolveApiVersion(exchange)) + .next(); + } + @Override public Comparable parseVersion(String version) { return this.versionParser.parseVersion(version); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/HeaderApiVersionResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/HeaderApiVersionResolver.java index 4d7871dec63..07863cefd91 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/HeaderApiVersionResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/HeaderApiVersionResolver.java @@ -26,7 +26,7 @@ import org.springframework.web.server.ServerWebExchange; * @author Rossen Stoyanchev * @since 7.0 */ -public class HeaderApiVersionResolver implements ApiVersionResolver { +public class HeaderApiVersionResolver implements SyncApiVersionResolver { private final String headerName; @@ -37,7 +37,7 @@ public class HeaderApiVersionResolver implements ApiVersionResolver { @Override - public @Nullable String resolveVersion(ServerWebExchange exchange) { + public @Nullable String resolveVersionValue(ServerWebExchange exchange) { return exchange.getRequest().getHeaders().getFirst(this.headerName); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/MediaTypeParamApiVersionResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/MediaTypeParamApiVersionResolver.java index c9967091088..db9b9acd3b5 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/MediaTypeParamApiVersionResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/MediaTypeParamApiVersionResolver.java @@ -29,7 +29,7 @@ import org.springframework.web.server.ServerWebExchange; * @author Rossen Stoyanchev * @since 7.0 */ -public class MediaTypeParamApiVersionResolver implements ApiVersionResolver { +public class MediaTypeParamApiVersionResolver implements SyncApiVersionResolver { private final MediaType compatibleMediaType; @@ -49,7 +49,7 @@ public class MediaTypeParamApiVersionResolver implements ApiVersionResolver { @Override - public @Nullable String resolveVersion(ServerWebExchange exchange) { + public @Nullable String resolveVersionValue(ServerWebExchange exchange) { HttpHeaders headers = exchange.getRequest().getHeaders(); for (MediaType mediaType : headers.getAccept()) { if (this.compatibleMediaType.isCompatibleWith(mediaType)) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/PathApiVersionResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/PathApiVersionResolver.java index 2da6819498b..06de39d20be 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/PathApiVersionResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/PathApiVersionResolver.java @@ -32,7 +32,7 @@ import org.springframework.web.server.ServerWebExchange; * @author Rossen Stoyanchev * @since 7.0 */ -public class PathApiVersionResolver implements ApiVersionResolver { +public class PathApiVersionResolver implements SyncApiVersionResolver { private final int pathSegmentIndex; @@ -49,7 +49,7 @@ public class PathApiVersionResolver implements ApiVersionResolver { @Override - public String resolveVersion(ServerWebExchange exchange) { + public String resolveVersionValue(ServerWebExchange exchange) { int i = 0; for (PathContainer.Element e : exchange.getRequest().getPath().pathWithinApplication().elements()) { if (e instanceof PathContainer.PathSegment && i++ == this.pathSegmentIndex) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/QueryApiVersionResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/QueryApiVersionResolver.java index 16606c75cbc..f81f832bfea 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/QueryApiVersionResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/QueryApiVersionResolver.java @@ -26,7 +26,7 @@ import org.springframework.web.server.ServerWebExchange; * @author Rossen Stoyanchev * @since 7.0 */ -public class QueryApiVersionResolver implements ApiVersionResolver { +public class QueryApiVersionResolver implements SyncApiVersionResolver { private final String queryParamName; @@ -37,7 +37,7 @@ public class QueryApiVersionResolver implements ApiVersionResolver { @Override - public @Nullable String resolveVersion(ServerWebExchange exchange) { + public @Nullable String resolveVersionValue(ServerWebExchange exchange) { return exchange.getRequest().getQueryParams().getFirst(this.queryParamName); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/accept/SyncApiVersionResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/SyncApiVersionResolver.java new file mode 100644 index 00000000000..bca707274d2 --- /dev/null +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/accept/SyncApiVersionResolver.java @@ -0,0 +1,57 @@ +/* + * Copyright 2002-present 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.web.reactive.accept; + +import org.jspecify.annotations.Nullable; +import reactor.core.publisher.Mono; + +import org.springframework.web.server.ServerWebExchange; + +/** + * An extension of {@link ApiVersionResolver}s for implementations that can + * resolve the version in an imperative way without blocking. + * + * @author Rossen Stoyanchev + * @since 7.0.3 + */ +@FunctionalInterface +public interface SyncApiVersionResolver extends ApiVersionResolver { + + /** + * {@inheritDoc} + *

This method delegates to the synchronous + * {@link #resolveVersionValue} and wraps the result as {@code Mono}. + */ + @Override + default Mono resolveApiVersion(ServerWebExchange exchange) { + return Mono.justOrEmpty(resolveVersionValue(exchange)); + } + + /** + * Resolve the version for the given exchange imperatively without blocking. + * @param exchange the current exchange + * @return the version value, or {@code null} if not found + */ + @Nullable String resolveVersionValue(ServerWebExchange exchange); + + @SuppressWarnings("removal") + @Override + default @Nullable String resolveVersion(ServerWebExchange exchange) { + return resolveVersionValue(exchange); + } + +} diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java index 80db15d5ad1..dd66f8427c8 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java @@ -185,54 +185,48 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport @Override public Mono getHandler(ServerWebExchange exchange) { - ApiVersionHolder versionHolder = initApiVersion(exchange); - return getHandlerInternal(exchange).map(handler -> { - if (logger.isDebugEnabled()) { - logger.debug(exchange.getLogPrefix() + "Mapped to " + handler); - } - if (versionHolder.hasError()) { - throw versionHolder.getError(); - } - ServerHttpRequest request = exchange.getRequest(); - if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { - CorsConfiguration config = (this.corsConfigurationSource != null ? - this.corsConfigurationSource.getCorsConfiguration(exchange) : null); - CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange); - config = (config != null ? config.combine(handlerConfig) : handlerConfig); - if (config != null) { - config.validateAllowCredentials(); - config.validateAllowPrivateNetwork(); - } - if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) { - return NO_OP_HANDLER; - } - } - if (getApiVersionStrategy() != null) { - if (versionHolder.hasVersion()) { - Comparable version = versionHolder.getVersion(); - getApiVersionStrategy().handleDeprecations(version, handler, exchange); - } - } - return handler; - }); + return initApiVersion(exchange).flatMap(versionHolder -> + getHandlerInternal(exchange).map(handler -> { + if (logger.isDebugEnabled()) { + logger.debug(exchange.getLogPrefix() + "Mapped to " + handler); + } + if (versionHolder.hasError()) { + throw versionHolder.getError(); + } + ServerHttpRequest request = exchange.getRequest(); + if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) { + CorsConfiguration config = (this.corsConfigurationSource != null ? + this.corsConfigurationSource.getCorsConfiguration(exchange) : null); + CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange); + config = (config != null ? config.combine(handlerConfig) : handlerConfig); + if (config != null) { + config.validateAllowCredentials(); + config.validateAllowPrivateNetwork(); + } + if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) { + return NO_OP_HANDLER; + } + } + if (getApiVersionStrategy() != null) { + if (versionHolder.hasVersion()) { + Comparable version = versionHolder.getVersion(); + getApiVersionStrategy().handleDeprecations(version, handler, exchange); + } + } + return handler; + })); } - private ApiVersionHolder initApiVersion(ServerWebExchange exchange) { - ApiVersionHolder versionHolder; - if (this.apiVersionStrategy == null) { - versionHolder = ApiVersionHolder.EMPTY; - } - else { - try { - Comparable version = this.apiVersionStrategy.resolveParseAndValidateVersion(exchange); - versionHolder = ApiVersionHolder.fromVersion(version); - } - catch (RuntimeException ex) { - versionHolder = ApiVersionHolder.fromError(ex); - } - } - exchange.getAttributes().put(API_VERSION_ATTRIBUTE, versionHolder); - return versionHolder; + private Mono initApiVersion(ServerWebExchange exchange) { + + Mono holderMono = (this.apiVersionStrategy != null ? + this.apiVersionStrategy.resolveParseAndValidateApiVersion(exchange) + .map(ApiVersionHolder::fromVersion) + .onErrorResume(RuntimeException.class, ex -> Mono.just(ApiVersionHolder.fromError(ex))) : + Mono.just(ApiVersionHolder.EMPTY)); + + return holderMono.doOnNext(holder -> + exchange.getAttributes().put(HandlerMapping.API_VERSION_ATTRIBUTE, holder)); } /** diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/accept/DefaultApiVersionStrategiesTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/accept/DefaultApiVersionStrategiesTests.java index 1b46a2bbb0a..99015f12f41 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/accept/DefaultApiVersionStrategiesTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/accept/DefaultApiVersionStrategiesTests.java @@ -21,6 +21,8 @@ import java.util.function.Predicate; import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; import org.springframework.web.accept.InvalidApiVersionException; import org.springframework.web.accept.MissingApiVersionException; @@ -30,11 +32,11 @@ import org.springframework.web.testfixture.server.MockServerWebExchange; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Unit tests for {@link org.springframework.web.accept.DefaultApiVersionStrategy}. * @author Rossen Stoyanchev + * @author Jonathan Kaplan */ public class DefaultApiVersionStrategiesTests { @@ -50,9 +52,11 @@ public class DefaultApiVersionStrategiesTests { @Test void missingRequiredVersion() { - assertThatThrownBy(() -> testValidate(null, apiVersionStrategy())) - .isInstanceOf(MissingApiVersionException.class) - .hasMessage("400 BAD_REQUEST \"API version is required.\""); + StepVerifier.create(testValidate(null, apiVersionStrategy())) + .expectErrorSatisfies(ex -> assertThat(ex) + .isInstanceOf(MissingApiVersionException.class) + .hasMessage(("400 BAD_REQUEST \"API version is required.\""))) + .verify(); } @Test @@ -60,21 +64,25 @@ public class DefaultApiVersionStrategiesTests { String version = "1.2"; DefaultApiVersionStrategy strategy = apiVersionStrategy(); strategy.addSupportedVersion(version); - testValidate(version, strategy); + Mono result = testValidate(version, strategy); + StepVerifier.create(result).expectNext("1.2.0").verifyComplete(); } @Test void validateSupportedVersionForDefaultVersion() { String defaultVersion = "1.2"; DefaultApiVersionStrategy strategy = apiVersionStrategy(defaultVersion, false, null); - testValidate(defaultVersion, strategy); + Mono result = testValidate(defaultVersion, strategy); + StepVerifier.create(result).expectNext("1.2.0").verifyComplete(); } @Test void validateUnsupportedVersion() { - assertThatThrownBy(() -> testValidate("1.2", apiVersionStrategy())) - .isInstanceOf(InvalidApiVersionException.class) - .hasMessage("400 BAD_REQUEST \"Invalid API version: '1.2.0'.\""); + StepVerifier.create(testValidate("1.2", apiVersionStrategy())) + .expectErrorSatisfies(ex -> assertThat(ex) + .isInstanceOf(InvalidApiVersionException.class) + .hasMessage(("400 BAD_REQUEST \"Invalid API version: '1.2.0'.\""))) + .verify(); } @Test @@ -82,7 +90,8 @@ public class DefaultApiVersionStrategiesTests { String version = "1.2"; DefaultApiVersionStrategy strategy = apiVersionStrategy(null, true, null); strategy.addMappedVersion(version); - testValidate(version, strategy); + Mono result = testValidate(version, strategy); + StepVerifier.create(result).expectNext("1.2.0").verifyComplete(); } @Test @@ -90,28 +99,32 @@ public class DefaultApiVersionStrategiesTests { String version = "1.2"; DefaultApiVersionStrategy strategy = apiVersionStrategy(); strategy.addMappedVersion(version); - assertThatThrownBy(() -> testValidate(version, strategy)).isInstanceOf(InvalidApiVersionException.class); + Mono result = testValidate(version, strategy); + StepVerifier.create(result).expectError(InvalidApiVersionException.class).verify(); } @Test void validateSupportedWithPredicate() { SemanticApiVersionParser.Version parsedVersion = parser.parseVersion("1.2"); - testValidate("1.2", apiVersionStrategy(null, false, version -> version.equals(parsedVersion))); + ApiVersionStrategy strategy = apiVersionStrategy(null, false, version -> version.equals(parsedVersion)); + Mono result = testValidate("1.2", strategy); + StepVerifier.create(result).expectNext("1.2.0").verifyComplete(); } @Test void validateUnsupportedWithPredicate() { - DefaultApiVersionStrategy strategy = apiVersionStrategy(null, false, version -> version.equals("1.2")); - assertThatThrownBy(() -> testValidate("1.2", strategy)).isInstanceOf(InvalidApiVersionException.class); + ApiVersionStrategy strategy = apiVersionStrategy(null, false, version -> version.equals("1.2")); + Mono result = testValidate("1.2", strategy); + StepVerifier.create(result).verifyError(InvalidApiVersionException.class); } @Test void versionRequiredAndDefaultVersionSet() { assertThatIllegalArgumentException() - .isThrownBy(() -> - new org.springframework.web.accept.DefaultApiVersionStrategy( - List.of(request -> request.getParameter("api-version")), new SemanticApiVersionParser(), - true, "1.2", true, version -> true, null)) + .isThrownBy(() -> { + ApiVersionResolver resolver = new QueryApiVersionResolver("api-version"); + new DefaultApiVersionStrategy(List.of(resolver), parser, true, "1.2", true, v -> true, null); + }) .withMessage("versionRequired cannot be set to true if a defaultVersion is also configured"); } @@ -124,16 +137,17 @@ public class DefaultApiVersionStrategiesTests { @Nullable Predicate> supportedVersionPredicate) { return new DefaultApiVersionStrategy( - List.of(exchange -> exchange.getRequest().getQueryParams().getFirst("api-version")), + List.of(new QueryApiVersionResolver("api-version")), parser, null, defaultVersion, detectSupportedVersions, supportedVersionPredicate, null); } - private void testValidate(@Nullable String version, DefaultApiVersionStrategy strategy) { - MockServerHttpRequest.BaseBuilder requestBuilder = MockServerHttpRequest.get("/"); + private Mono testValidate(@Nullable String version, ApiVersionStrategy strategy) { + MockServerHttpRequest.BaseBuilder builder = MockServerHttpRequest.get("/"); if (version != null) { - requestBuilder.queryParam("api-version", version); + builder.queryParam("api-version", version); } - strategy.resolveParseAndValidateVersion(MockServerWebExchange.builder(requestBuilder).build()); + MockServerWebExchange exchange = MockServerWebExchange.builder(builder).build(); + return strategy.resolveParseAndValidateApiVersion(exchange).map(Object::toString); } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RequestPredicatesTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RequestPredicatesTests.java index 7a2fbc496ad..765a4e811e9 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RequestPredicatesTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RequestPredicatesTests.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.function.Function; import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -381,7 +382,7 @@ class RequestPredicatesTests { private static DefaultApiVersionStrategy apiVersionStrategy() { return new DefaultApiVersionStrategy( - List.of(exchange -> null), new SemanticApiVersionParser(), true, null, false, null, null); + List.of(exchange -> Mono.empty()), new SemanticApiVersionParser(), true, null, false, null, null); } } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/VersionRequestConditionTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/VersionRequestConditionTests.java index 4fe3ba26221..3267cb89074 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/VersionRequestConditionTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/condition/VersionRequestConditionTests.java @@ -29,6 +29,7 @@ import org.springframework.web.accept.NotAcceptableApiVersionException; import org.springframework.web.accept.SemanticApiVersionParser; import org.springframework.web.reactive.HandlerMapping; import org.springframework.web.reactive.accept.DefaultApiVersionStrategy; +import org.springframework.web.reactive.accept.QueryApiVersionResolver; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest; import org.springframework.web.testfixture.server.MockServerWebExchange; @@ -52,7 +53,7 @@ public class VersionRequestConditionTests { private static DefaultApiVersionStrategy initVersionStrategy(@Nullable String defaultVersion) { return new DefaultApiVersionStrategy( - List.of(exchange -> exchange.getRequest().getQueryParams().getFirst("api-version")), + List.of(new QueryApiVersionResolver("api-version")), new SemanticApiVersionParser(), null, defaultVersion, false, null, null); }