Resolve API version in RequestMappingHandlerMapping
API version resolution and parsing is already applied as long as an ApiVersionStrategy is configured and irrespective of whether a given RequestMapping has a version or not. RequestMappingHandlerMapping also needs to be aware of the API version in order to apply deprecated version handling. So it is better to resolve, parse, and validate the version in the beginning of handler mapping rather than in the first call to any VersionRequestCondition. Closes gh-35049
This commit is contained in:
parent
492e51f3ba
commit
ffdf941219
|
@ -36,7 +36,7 @@ public class DefaultApiVersionStrategiesTests {
|
|||
|
||||
|
||||
@Test
|
||||
void defaultVersion() {
|
||||
void defaultVersionIsParsed() {
|
||||
SemanticApiVersionParser.Version version = this.parser.parseVersion("1.2.3");
|
||||
ApiVersionStrategy strategy = initVersionStrategy(version.toString());
|
||||
|
||||
|
@ -44,21 +44,29 @@ public class DefaultApiVersionStrategiesTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void supportedVersions() {
|
||||
SemanticApiVersionParser.Version v1 = this.parser.parseVersion("1");
|
||||
SemanticApiVersionParser.Version v2 = this.parser.parseVersion("2");
|
||||
SemanticApiVersionParser.Version v9 = this.parser.parseVersion("9");
|
||||
void validateSupportedVersion() {
|
||||
SemanticApiVersionParser.Version v12 = this.parser.parseVersion("1.2");
|
||||
|
||||
DefaultApiVersionStrategy strategy = initVersionStrategy(null);
|
||||
strategy.addSupportedVersion(v1.toString());
|
||||
strategy.addSupportedVersion(v2.toString());
|
||||
strategy.addSupportedVersion(v12.toString());
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
strategy.validateVersion(v1, request);
|
||||
strategy.validateVersion(v2, request);
|
||||
strategy.validateVersion(v12, request);
|
||||
}
|
||||
|
||||
assertThatThrownBy(() -> strategy.validateVersion(v9, request))
|
||||
.isInstanceOf(InvalidApiVersionException.class);
|
||||
@Test
|
||||
void validateUnsupportedVersion() {
|
||||
assertThatThrownBy(() -> initVersionStrategy(null).validateVersion("1.2", new MockHttpServletRequest()))
|
||||
.isInstanceOf(InvalidApiVersionException.class)
|
||||
.hasMessage("400 BAD_REQUEST \"Invalid API version: '1.2'.\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingRequiredVersion() {
|
||||
DefaultApiVersionStrategy strategy = initVersionStrategy(null);
|
||||
assertThatThrownBy(() -> strategy.validateVersion(null, new MockHttpServletRequest()))
|
||||
.isInstanceOf(MissingApiVersionException.class)
|
||||
.hasMessage("400 BAD_REQUEST \"API version is required.\"");
|
||||
}
|
||||
|
||||
private static DefaultApiVersionStrategy initVersionStrategy(@Nullable String defaultValue) {
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.jspecify.annotations.Nullable;
|
|||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.accept.InvalidApiVersionException;
|
||||
import org.springframework.web.accept.NotAcceptableApiVersionException;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
|
@ -42,21 +41,12 @@ import org.springframework.web.server.ServerWebExchange;
|
|||
*/
|
||||
public final class VersionRequestCondition extends AbstractRequestCondition<VersionRequestCondition> {
|
||||
|
||||
private static final String VERSION_ATTRIBUTE_NAME = VersionRequestCondition.class.getName() + ".VERSION";
|
||||
|
||||
private static final String NO_VERSION_ATTRIBUTE = "NO_VERSION";
|
||||
|
||||
private static final ApiVersionStrategy NO_OP_VERSION_STRATEGY = new NoOpApiVersionStrategy();
|
||||
|
||||
|
||||
private final @Nullable String versionValue;
|
||||
|
||||
private final @Nullable Object version;
|
||||
|
||||
private final boolean baselineVersion;
|
||||
|
||||
private final ApiVersionStrategy versionStrategy;
|
||||
|
||||
private final Set<String> content;
|
||||
|
||||
|
||||
|
@ -67,14 +57,12 @@ public final class VersionRequestCondition extends AbstractRequestCondition<Vers
|
|||
public VersionRequestCondition(@Nullable String version, @Nullable ApiVersionStrategy strategy) {
|
||||
if (StringUtils.hasText(version)) {
|
||||
Assert.isTrue(strategy != null, "ApiVersionStrategy is required for mapping by version");
|
||||
this.versionStrategy = strategy;
|
||||
this.baselineVersion = version.endsWith("+");
|
||||
this.versionValue = updateVersion(version, this.baselineVersion);
|
||||
this.version = this.versionStrategy.parseVersion(this.versionValue);
|
||||
this.version = strategy.parseVersion(this.versionValue);
|
||||
this.content = Set.of(version);
|
||||
}
|
||||
else {
|
||||
this.versionStrategy = (strategy != null ? strategy : NO_OP_VERSION_STRATEGY);
|
||||
this.versionValue = null;
|
||||
this.version = null;
|
||||
this.baselineVersion = false;
|
||||
|
@ -111,37 +99,19 @@ public final class VersionRequestCondition extends AbstractRequestCondition<Vers
|
|||
|
||||
@Override
|
||||
public @Nullable VersionRequestCondition getMatchingCondition(ServerWebExchange exchange) {
|
||||
Comparable<?> requestVersion = exchange.getAttribute(VERSION_ATTRIBUTE_NAME);
|
||||
if (requestVersion == null) {
|
||||
String value = this.versionStrategy.resolveVersion(exchange);
|
||||
requestVersion = (value != null ? parseVersion(value) : this.versionStrategy.getDefaultVersion());
|
||||
this.versionStrategy.validateVersion(requestVersion, exchange);
|
||||
requestVersion = (requestVersion != null ? requestVersion : NO_VERSION_ATTRIBUTE);
|
||||
exchange.getAttributes().put(VERSION_ATTRIBUTE_NAME, requestVersion);
|
||||
}
|
||||
Comparable<?> requestVersion = exchange.getAttribute(HandlerMapping.API_VERSION_ATTRIBUTE);
|
||||
|
||||
if (this.version == null || requestVersion == NO_VERSION_ATTRIBUTE) {
|
||||
if (this.version == null || requestVersion == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
exchange.getAttributes().put(HandlerMapping.API_VERSION_ATTRIBUTE, requestVersion);
|
||||
|
||||
// At this stage, match all versions as baseline versions.
|
||||
// Strict matching for fixed versions is enforced at the end in handleMatch.
|
||||
// Always use a baseline match here in order to select the highest version (baseline or fixed)
|
||||
// The fixed version match is enforced at the end in handleMatch()
|
||||
|
||||
int result = compareVersions(this.version, requestVersion);
|
||||
return (result <= 0 ? this : null);
|
||||
}
|
||||
|
||||
private Comparable<?> parseVersion(String value) {
|
||||
try {
|
||||
return this.versionStrategy.parseVersion(value);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new InvalidApiVersionException(value, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <V extends Comparable<V>> int compareVersions(Object v1, Object v2) {
|
||||
return ((V) v1).compareTo((V) v2);
|
||||
|
@ -178,7 +148,7 @@ public final class VersionRequestCondition extends AbstractRequestCondition<Vers
|
|||
*/
|
||||
public void handleMatch(ServerWebExchange exchange) {
|
||||
if (this.version != null && !this.baselineVersion) {
|
||||
Comparable<?> version = exchange.getAttribute(VERSION_ATTRIBUTE_NAME);
|
||||
Comparable<?> version = exchange.getAttribute(HandlerMapping.API_VERSION_ATTRIBUTE);
|
||||
Assert.state(version != null, "No API version attribute");
|
||||
if (!this.version.equals(version)) {
|
||||
throw new NotAcceptableApiVersionException(version.toString());
|
||||
|
@ -186,31 +156,4 @@ public final class VersionRequestCondition extends AbstractRequestCondition<Vers
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class NoOpApiVersionStrategy implements ApiVersionStrategy {
|
||||
|
||||
@Override
|
||||
public @Nullable String resolveVersion(ServerWebExchange exchange) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parseVersion(String version) {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateVersion(@Nullable Comparable<?> requestVersion, ServerWebExchange exchange) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Comparable<?> getDefaultVersion() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDeprecations(Comparable<?> version, ServerWebExchange exchange) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.function.Predicate;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
|
@ -41,6 +42,7 @@ import org.springframework.util.Assert;
|
|||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
import org.springframework.web.accept.InvalidApiVersionException;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
@ -173,6 +175,37 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
|||
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange) {
|
||||
if (this.apiVersionStrategy != null) {
|
||||
Comparable<?> requestVersion = exchange.getAttribute(API_VERSION_ATTRIBUTE);
|
||||
if (requestVersion == null) {
|
||||
requestVersion = getApiVersion(exchange, this.apiVersionStrategy);
|
||||
if (requestVersion != null) {
|
||||
exchange.getAttributes().put(API_VERSION_ATTRIBUTE, requestVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.getHandlerInternal(exchange);
|
||||
}
|
||||
|
||||
private static @Nullable Comparable<?> getApiVersion(
|
||||
ServerWebExchange exchange, ApiVersionStrategy versionStrategy) {
|
||||
|
||||
String value = versionStrategy.resolveVersion(exchange);
|
||||
if (value == null) {
|
||||
return versionStrategy.getDefaultVersion();
|
||||
}
|
||||
try {
|
||||
Comparable<?> version = versionStrategy.parseVersion(value);
|
||||
versionStrategy.validateVersion(version, exchange);
|
||||
return version;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new InvalidApiVersionException(value, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses type-level and method-level {@link RequestMapping @RequestMapping}
|
||||
* and {@link HttpExchange @HttpExchange} annotations to create the
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.jspecify.annotations.Nullable;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.web.accept.InvalidApiVersionException;
|
||||
import org.springframework.web.accept.MissingApiVersionException;
|
||||
import org.springframework.web.accept.SemanticApiVersionParser;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest;
|
||||
|
@ -59,7 +60,8 @@ public class DefaultApiVersionStrategiesTests {
|
|||
@Test
|
||||
void validateUnsupportedVersion() {
|
||||
assertThatThrownBy(() -> validateVersion("1.2", apiVersionStrategy()))
|
||||
.isInstanceOf(InvalidApiVersionException.class);
|
||||
.isInstanceOf(InvalidApiVersionException.class)
|
||||
.hasMessage("400 BAD_REQUEST \"Invalid API version: '1.2.0'.\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -80,6 +82,14 @@ public class DefaultApiVersionStrategiesTests {
|
|||
.isInstanceOf(InvalidApiVersionException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingRequiredVersion() {
|
||||
DefaultApiVersionStrategy strategy = apiVersionStrategy();
|
||||
assertThatThrownBy(() -> strategy.validateVersion(null, exchange))
|
||||
.isInstanceOf(MissingApiVersionException.class)
|
||||
.hasMessage("400 BAD_REQUEST \"API version is required.\"");
|
||||
}
|
||||
|
||||
private static DefaultApiVersionStrategy apiVersionStrategy() {
|
||||
return apiVersionStrategy(null, false);
|
||||
}
|
||||
|
|
|
@ -23,10 +23,9 @@ import org.jspecify.annotations.Nullable;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.web.accept.InvalidApiVersionException;
|
||||
import org.springframework.web.accept.MissingApiVersionException;
|
||||
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.server.ServerWebExchange;
|
||||
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest;
|
||||
|
@ -99,12 +98,6 @@ public class VersionRequestConditionTests {
|
|||
|
||||
testMatch("v1.1", condition, true, false);
|
||||
testMatch("v1.3", condition, true, false);
|
||||
|
||||
assertThatThrownBy(() -> condition.getMatchingCondition(exchangeWithVersion("1.2")))
|
||||
.isInstanceOf(InvalidApiVersionException.class);
|
||||
|
||||
assertThatThrownBy(() -> condition.getMatchingCondition(MockServerWebExchange.from(MockServerHttpRequest.get("/"))))
|
||||
.isInstanceOf(MissingApiVersionException.class);
|
||||
}
|
||||
|
||||
private void testMatch(
|
||||
|
@ -128,12 +121,6 @@ public class VersionRequestConditionTests {
|
|||
condition.handleMatch(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingRequiredVersion() {
|
||||
assertThatThrownBy(() -> condition("1.2").getMatchingCondition(exchange()))
|
||||
.hasMessage("400 BAD_REQUEST \"API version is required.\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultVersion() {
|
||||
String version = "1.2";
|
||||
|
@ -144,12 +131,6 @@ public class VersionRequestConditionTests {
|
|||
assertThat(match).isSameAs(condition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unsupportedVersion() {
|
||||
assertThatThrownBy(() -> condition("1.2").getMatchingCondition(exchangeWithVersion("1.3")))
|
||||
.hasMessage("400 BAD_REQUEST \"Invalid API version: '1.3.0'.\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void compare() {
|
||||
testCompare("1.1", "1", "1.1");
|
||||
|
@ -181,8 +162,10 @@ public class VersionRequestConditionTests {
|
|||
}
|
||||
|
||||
private ServerWebExchange exchangeWithVersion(String v) {
|
||||
return MockServerWebExchange.from(
|
||||
MockServerHttpRequest.get("/path").queryParam("api-version", v));
|
||||
Comparable<?> version = this.strategy.parseVersion(v);
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/path"));
|
||||
exchange.getAttributes().put(HandlerMapping.API_VERSION_ATTRIBUTE, version);
|
||||
return exchange;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,13 +21,11 @@ import java.util.Collections;
|
|||
import java.util.Set;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.accept.ApiVersionStrategy;
|
||||
import org.springframework.web.accept.InvalidApiVersionException;
|
||||
import org.springframework.web.accept.NotAcceptableApiVersionException;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
@ -42,21 +40,12 @@ import org.springframework.web.servlet.HandlerMapping;
|
|||
*/
|
||||
public final class VersionRequestCondition extends AbstractRequestCondition<VersionRequestCondition> {
|
||||
|
||||
private static final String VERSION_ATTRIBUTE_NAME = VersionRequestCondition.class.getName() + ".VERSION";
|
||||
|
||||
private static final String NO_VERSION_ATTRIBUTE = "NO_VERSION";
|
||||
|
||||
private static final ApiVersionStrategy NO_OP_VERSION_STRATEGY = new NoOpApiVersionStrategy();
|
||||
|
||||
|
||||
private final @Nullable String versionValue;
|
||||
|
||||
private final @Nullable Comparable<?> version;
|
||||
|
||||
private final boolean baselineVersion;
|
||||
|
||||
private final ApiVersionStrategy versionStrategy;
|
||||
|
||||
private final Set<String> content;
|
||||
|
||||
|
||||
|
@ -67,14 +56,12 @@ public final class VersionRequestCondition extends AbstractRequestCondition<Vers
|
|||
public VersionRequestCondition(@Nullable String version, @Nullable ApiVersionStrategy strategy) {
|
||||
if (StringUtils.hasText(version)) {
|
||||
Assert.isTrue(strategy != null, "ApiVersionStrategy is required for mapping by version");
|
||||
this.versionStrategy = strategy;
|
||||
this.baselineVersion = version.endsWith("+");
|
||||
this.versionValue = updateVersion(version, this.baselineVersion);
|
||||
this.version = this.versionStrategy.parseVersion(this.versionValue);
|
||||
this.version = strategy.parseVersion(this.versionValue);
|
||||
this.content = Set.of(version);
|
||||
}
|
||||
else {
|
||||
this.versionStrategy = (strategy != null ? strategy : NO_OP_VERSION_STRATEGY);
|
||||
this.versionValue = null;
|
||||
this.version = null;
|
||||
this.baselineVersion = false;
|
||||
|
@ -111,37 +98,19 @@ public final class VersionRequestCondition extends AbstractRequestCondition<Vers
|
|||
|
||||
@Override
|
||||
public @Nullable VersionRequestCondition getMatchingCondition(HttpServletRequest request) {
|
||||
Comparable<?> requestVersion = (Comparable<?>) request.getAttribute(VERSION_ATTRIBUTE_NAME);
|
||||
if (requestVersion == null) {
|
||||
String value = this.versionStrategy.resolveVersion(request);
|
||||
requestVersion = (value != null ? parseVersion(value) : this.versionStrategy.getDefaultVersion());
|
||||
this.versionStrategy.validateVersion(requestVersion, request);
|
||||
requestVersion = (requestVersion != null ? requestVersion : NO_VERSION_ATTRIBUTE);
|
||||
request.setAttribute(VERSION_ATTRIBUTE_NAME, requestVersion);
|
||||
}
|
||||
Comparable<?> requestVersion = (Comparable<?>) request.getAttribute(HandlerMapping.API_VERSION_ATTRIBUTE);
|
||||
|
||||
if (this.version == null || requestVersion == NO_VERSION_ATTRIBUTE) {
|
||||
if (this.version == null || requestVersion == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
request.setAttribute(HandlerMapping.API_VERSION_ATTRIBUTE, requestVersion);
|
||||
|
||||
// At this stage, match all versions as baseline versions.
|
||||
// Strict matching for fixed versions is enforced at the end in handleMatch.
|
||||
// Always use a baseline match here in order to select the highest version (baseline or fixed)
|
||||
// The fixed version match is enforced at the end in handleMatch()
|
||||
|
||||
int result = compareVersions(this.version, requestVersion);
|
||||
return (result <= 0 ? this : null);
|
||||
}
|
||||
|
||||
private Comparable<?> parseVersion(String value) {
|
||||
try {
|
||||
return this.versionStrategy.parseVersion(value);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new InvalidApiVersionException(value, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <V extends Comparable<V>> int compareVersions(Object v1, Object v2) {
|
||||
return ((V) v1).compareTo((V) v2);
|
||||
|
@ -178,7 +147,7 @@ public final class VersionRequestCondition extends AbstractRequestCondition<Vers
|
|||
*/
|
||||
public void handleMatch(HttpServletRequest request) {
|
||||
if (this.version != null && !this.baselineVersion) {
|
||||
Comparable<?> version = (Comparable<?>) request.getAttribute(VERSION_ATTRIBUTE_NAME);
|
||||
Comparable<?> version = (Comparable<?>) request.getAttribute(HandlerMapping.API_VERSION_ATTRIBUTE);
|
||||
Assert.state(version != null, "No API version attribute");
|
||||
if (!this.version.equals(version)) {
|
||||
throw new NotAcceptableApiVersionException(version.toString());
|
||||
|
@ -186,31 +155,4 @@ public final class VersionRequestCondition extends AbstractRequestCondition<Vers
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class NoOpApiVersionStrategy implements ApiVersionStrategy {
|
||||
|
||||
@Override
|
||||
public @Nullable String resolveVersion(HttpServletRequest request) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parseVersion(String version) {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateVersion(@Nullable Comparable<?> requestVersion, HttpServletRequest request) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Comparable<?> getDefaultVersion() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDeprecations(Comparable<?> version, HttpServletRequest request, HttpServletResponse response) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.springframework.util.StringValueResolver;
|
|||
import org.springframework.web.accept.ApiVersionStrategy;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.accept.DefaultApiVersionStrategy;
|
||||
import org.springframework.web.accept.InvalidApiVersionException;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
@ -200,6 +201,38 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
|||
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected @Nullable HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
|
||||
if (this.apiVersionStrategy != null) {
|
||||
Comparable<?> requestVersion = (Comparable<?>) request.getAttribute(API_VERSION_ATTRIBUTE);
|
||||
if (requestVersion == null) {
|
||||
requestVersion = getApiVersion(request, this.apiVersionStrategy);
|
||||
if (requestVersion != null) {
|
||||
request.setAttribute(API_VERSION_ATTRIBUTE, requestVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.getHandlerInternal(request);
|
||||
}
|
||||
|
||||
private static @Nullable Comparable<?> getApiVersion(
|
||||
HttpServletRequest request, ApiVersionStrategy versionStrategy) {
|
||||
|
||||
String value = versionStrategy.resolveVersion(request);
|
||||
if (value == null) {
|
||||
return versionStrategy.getDefaultVersion();
|
||||
}
|
||||
try {
|
||||
Comparable<?> version = versionStrategy.parseVersion(value);
|
||||
versionStrategy.validateVersion(version, request);
|
||||
return version;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new InvalidApiVersionException(value, null, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses type-level and method-level {@link RequestMapping @RequestMapping}
|
||||
* and {@link HttpExchange @HttpExchange} annotations to create the
|
||||
|
|
|
@ -24,10 +24,9 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.web.accept.DefaultApiVersionStrategy;
|
||||
import org.springframework.web.accept.InvalidApiVersionException;
|
||||
import org.springframework.web.accept.MissingApiVersionException;
|
||||
import org.springframework.web.accept.NotAcceptableApiVersionException;
|
||||
import org.springframework.web.accept.SemanticApiVersionParser;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -97,12 +96,6 @@ public class VersionRequestConditionTests {
|
|||
|
||||
testMatch("v1.1", condition, true, false);
|
||||
testMatch("v1.3", condition, true, false);
|
||||
|
||||
assertThatThrownBy(() -> condition.getMatchingCondition(requestWithVersion("1.2")))
|
||||
.isInstanceOf(InvalidApiVersionException.class);
|
||||
|
||||
assertThatThrownBy(() -> condition.getMatchingCondition(new MockHttpServletRequest("GET", "/path")))
|
||||
.isInstanceOf(MissingApiVersionException.class);
|
||||
}
|
||||
|
||||
private void testMatch(
|
||||
|
@ -126,12 +119,6 @@ public class VersionRequestConditionTests {
|
|||
condition.handleMatch(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingRequiredVersion() {
|
||||
assertThatThrownBy(() -> condition("1.2").getMatchingCondition(new MockHttpServletRequest("GET", "/path")))
|
||||
.hasMessage("400 BAD_REQUEST \"API version is required.\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void defaultVersion() {
|
||||
String version = "1.2";
|
||||
|
@ -142,12 +129,6 @@ public class VersionRequestConditionTests {
|
|||
assertThat(match).isSameAs(condition);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unsupportedVersion() {
|
||||
assertThatThrownBy(() -> condition("1.2").getMatchingCondition(requestWithVersion("1.3")))
|
||||
.hasMessage("400 BAD_REQUEST \"Invalid API version: '1.3.0'.\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void compare() {
|
||||
testCompare("1.1", "1", "1.1");
|
||||
|
@ -176,7 +157,7 @@ public class VersionRequestConditionTests {
|
|||
|
||||
private MockHttpServletRequest requestWithVersion(String v) {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/path");
|
||||
request.addParameter("api-version", v);
|
||||
request.setAttribute(HandlerMapping.API_VERSION_ATTRIBUTE, this.strategy.parseVersion(v));
|
||||
return request;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue