Rename ApiDeprecationHandler to insert "Version"

The name is a bit long, but it is necessary to indicate it's a handler
for a deprecation version, and the decision is based on the version,
not an individual endpoint.

See gh-35049
This commit is contained in:
rstoyanchev 2025-06-24 10:21:59 +01:00
parent 7606a929c9
commit 785aab8ad5
14 changed files with 47 additions and 47 deletions

View File

@ -22,13 +22,13 @@ import jakarta.servlet.http.HttpServletResponse;
/** /**
* Contract to add handling of requests with a deprecated API version. Typically, * Contract to add handling of requests with a deprecated API version. Typically,
* this involves use of response headers to send hints and information about * this involves use of response headers to send hints and information about
* the deprecation to clients. * the deprecated version to clients.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 7.0 * @since 7.0
* @see StandardApiDeprecationHandler * @see StandardApiVersionDeprecationHandler
*/ */
public interface ApiDeprecationHandler { public interface ApiVersionDeprecationHandler {
/** /**
* Check if the requested API version is deprecated, and if so handle it * Check if the requested API version is deprecated, and if so handle it

View File

@ -69,7 +69,7 @@ public interface ApiVersionStrategy {
* @param version the resolved and parsed request version * @param version the resolved and parsed request version
* @param request the current request * @param request the current request
* @param response the current response * @param response the current response
* @see ApiDeprecationHandler * @see ApiVersionDeprecationHandler
*/ */
void handleDeprecations(Comparable<?> version, HttpServletRequest request, HttpServletResponse response); void handleDeprecations(Comparable<?> version, HttpServletRequest request, HttpServletResponse response);

View File

@ -44,7 +44,7 @@ public class DefaultApiVersionStrategy implements ApiVersionStrategy {
private final @Nullable Comparable<?> defaultVersion; private final @Nullable Comparable<?> defaultVersion;
private final @Nullable ApiDeprecationHandler deprecationHandler; private final @Nullable ApiVersionDeprecationHandler deprecationHandler;
private final Set<Comparable<?>> supportedVersions = new TreeSet<>(); private final Set<Comparable<?>> supportedVersions = new TreeSet<>();
@ -65,7 +65,7 @@ public class DefaultApiVersionStrategy implements ApiVersionStrategy {
public DefaultApiVersionStrategy( public DefaultApiVersionStrategy(
List<ApiVersionResolver> versionResolvers, ApiVersionParser<?> versionParser, List<ApiVersionResolver> versionResolvers, ApiVersionParser<?> versionParser,
boolean versionRequired, @Nullable String defaultVersion, boolean versionRequired, @Nullable String defaultVersion,
@Nullable ApiDeprecationHandler deprecationHandler) { @Nullable ApiVersionDeprecationHandler deprecationHandler) {
Assert.notEmpty(versionResolvers, "At least one ApiVersionResolver is required"); Assert.notEmpty(versionResolvers, "At least one ApiVersionResolver is required");
Assert.notNull(versionParser, "ApiVersionParser is required"); Assert.notNull(versionParser, "ApiVersionParser is required");

View File

@ -33,7 +33,7 @@ import org.springframework.http.MediaType;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
* {@code ApiDeprecationHandler} based on * {@code ApiVersionDeprecationHandler} based on
* <a href="https://datatracker.ietf.org/doc/html/rfc9745">RFC 9745</a> and * <a href="https://datatracker.ietf.org/doc/html/rfc9745">RFC 9745</a> and
* <a href="https://datatracker.ietf.org/doc/html/rfc8594">RFC 8594</a> that * <a href="https://datatracker.ietf.org/doc/html/rfc8594">RFC 8594</a> that
* provides the option to set the "Deprecation" and "Sunset" response headers, * provides the option to set the "Deprecation" and "Sunset" response headers,
@ -45,7 +45,7 @@ import org.springframework.util.Assert;
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 7.0 * @since 7.0
*/ */
public class StandardApiDeprecationHandler implements ApiDeprecationHandler { public class StandardApiVersionDeprecationHandler implements ApiVersionDeprecationHandler {
private final ApiVersionParser<?> versionParser; private final ApiVersionParser<?> versionParser;
@ -57,9 +57,9 @@ public class StandardApiDeprecationHandler implements ApiDeprecationHandler {
* <p>By default, {@link SemanticApiVersionParser} is used to parse configured * <p>By default, {@link SemanticApiVersionParser} is used to parse configured
* API versions, so those can be compared to request versions parsed at runtime. * API versions, so those can be compared to request versions parsed at runtime.
* If you have a custom parser, then please use the * If you have a custom parser, then please use the
* {@link #StandardApiDeprecationHandler(ApiVersionParser)} constructor. * {@link #StandardApiVersionDeprecationHandler(ApiVersionParser)} constructor.
*/ */
public StandardApiDeprecationHandler() { public StandardApiVersionDeprecationHandler() {
this(new SemanticApiVersionParser()); this(new SemanticApiVersionParser());
} }
@ -68,7 +68,7 @@ public class StandardApiDeprecationHandler implements ApiDeprecationHandler {
* This needs to be the same as the parser type used at runtime to parse * This needs to be the same as the parser type used at runtime to parse
* request versions. * request versions.
*/ */
public StandardApiDeprecationHandler(ApiVersionParser<?> parser) { public StandardApiVersionDeprecationHandler(ApiVersionParser<?> parser) {
this.versionParser = parser; this.versionParser = parser;
} }
@ -109,7 +109,7 @@ public class StandardApiDeprecationHandler implements ApiDeprecationHandler {
@Override @Override
public String toString() { public String toString() {
return "StandardApiDeprecationHandler " + this.infos.values(); return "StandardApiVersionDeprecationHandler " + this.infos.values();
} }
@ -122,7 +122,7 @@ public class StandardApiDeprecationHandler implements ApiDeprecationHandler {
private VersionSpec(Comparable<?> version) { private VersionSpec(Comparable<?> version) {
this.version = version; this.version = version;
StandardApiDeprecationHandler.this.infos.put(version, new VersionInfo(version)); StandardApiVersionDeprecationHandler.this.infos.put(version, new VersionInfo(version));
} }
/** /**
@ -198,7 +198,7 @@ public class StandardApiDeprecationHandler implements ApiDeprecationHandler {
} }
private VersionSpec map(Function<VersionInfo, VersionInfo> function) { private VersionSpec map(Function<VersionInfo, VersionInfo> function) {
StandardApiDeprecationHandler.this.infos.compute(this.version, (version, versionInfo) -> { StandardApiVersionDeprecationHandler.this.infos.compute(this.version, (version, versionInfo) -> {
Assert.state(versionInfo != null, "No VersionInfo"); Assert.state(versionInfo != null, "No VersionInfo");
return function.apply(versionInfo); return function.apply(versionInfo);
}); });

View File

@ -28,10 +28,10 @@ import org.springframework.web.testfixture.servlet.MockHttpServletResponse;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Unit tests for {@link StandardApiDeprecationHandler}. * Unit tests for {@link StandardApiVersionDeprecationHandler}.
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
*/ */
public class StandardApiDeprecationHandlerTests { public class StandardApiVersionDeprecationHandlerTests {
private final MockHttpServletRequest request = new MockHttpServletRequest(); private final MockHttpServletRequest request = new MockHttpServletRequest();
@ -45,7 +45,7 @@ public class StandardApiDeprecationHandlerTests {
String sunsetUrl = "https://example.org/sunset"; String sunsetUrl = "https://example.org/sunset";
ApiVersionParser<String> parser = version -> version; ApiVersionParser<String> parser = version -> version;
StandardApiDeprecationHandler handler = new StandardApiDeprecationHandler(parser); StandardApiVersionDeprecationHandler handler = new StandardApiVersionDeprecationHandler(parser);
handler.configureVersion("1.1") handler.configureVersion("1.1")
.setDeprecationDate(getDate("Fri, 30 Jun 2023 23:59:59 GMT")) .setDeprecationDate(getDate("Fri, 30 Jun 2023 23:59:59 GMT"))

View File

@ -21,12 +21,12 @@ import org.springframework.web.server.ServerWebExchange;
/** /**
* Contract to add handling of requests with a deprecated API version. Typically, * Contract to add handling of requests with a deprecated API version. Typically,
* this involves use of response headers to send hints and information about * this involves use of response headers to send hints and information about
* the deprecation to clients. * the deprecated version to clients.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 7.0 * @since 7.0
*/ */
public interface ApiDeprecationHandler { public interface ApiVersionDeprecationHandler {
/** /**
* Check if the requested API version is deprecated, and if so handle it * Check if the requested API version is deprecated, and if so handle it

View File

@ -70,7 +70,7 @@ public interface ApiVersionStrategy {
* to specify relevant dates and provide links to further details. * to specify relevant dates and provide links to further details.
* @param version the resolved and parsed request version * @param version the resolved and parsed request version
* @param exchange the current exchange * @param exchange the current exchange
* @see ApiDeprecationHandler * @see ApiVersionDeprecationHandler
*/ */
void handleDeprecations(Comparable<?> version, ServerWebExchange exchange); void handleDeprecations(Comparable<?> version, ServerWebExchange exchange);

View File

@ -52,7 +52,7 @@ public class DefaultApiVersionStrategy implements ApiVersionStrategy {
private final Set<Comparable<?>> detectedVersions = new TreeSet<>(); private final Set<Comparable<?>> detectedVersions = new TreeSet<>();
private final @Nullable ApiDeprecationHandler deprecationHandler; private final @Nullable ApiVersionDeprecationHandler deprecationHandler;
/** /**
@ -74,7 +74,7 @@ public class DefaultApiVersionStrategy implements ApiVersionStrategy {
public DefaultApiVersionStrategy( public DefaultApiVersionStrategy(
List<ApiVersionResolver> versionResolvers, ApiVersionParser<?> versionParser, List<ApiVersionResolver> versionResolvers, ApiVersionParser<?> versionParser,
boolean versionRequired, @Nullable String defaultVersion, boolean detectSupportedVersions, boolean versionRequired, @Nullable String defaultVersion, boolean detectSupportedVersions,
@Nullable ApiDeprecationHandler deprecationHandler) { @Nullable ApiVersionDeprecationHandler deprecationHandler) {
Assert.notEmpty(versionResolvers, "At least one ApiVersionResolver is required"); Assert.notEmpty(versionResolvers, "At least one ApiVersionResolver is required");
Assert.notNull(versionParser, "ApiVersionParser is required"); Assert.notNull(versionParser, "ApiVersionParser is required");

View File

@ -34,7 +34,7 @@ import org.springframework.web.accept.SemanticApiVersionParser;
import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebExchange;
/** /**
* {@code ApiDeprecationHandler} based on * {@code ApiVersionDeprecationHandler} based on
* <a href="https://datatracker.ietf.org/doc/html/rfc9745">RFC 9745</a> and * <a href="https://datatracker.ietf.org/doc/html/rfc9745">RFC 9745</a> and
* <a href="https://datatracker.ietf.org/doc/html/rfc8594">RFC 8594</a> that * <a href="https://datatracker.ietf.org/doc/html/rfc8594">RFC 8594</a> that
* provides the option to set the "Deprecation" and "Sunset" response headers, * provides the option to set the "Deprecation" and "Sunset" response headers,
@ -46,7 +46,7 @@ import org.springframework.web.server.ServerWebExchange;
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 7.0 * @since 7.0
*/ */
public class StandardApiDeprecationHandler implements ApiDeprecationHandler { public class StandardApiVersionDeprecationHandler implements ApiVersionDeprecationHandler {
private final ApiVersionParser<?> versionParser; private final ApiVersionParser<?> versionParser;
@ -58,9 +58,9 @@ public class StandardApiDeprecationHandler implements ApiDeprecationHandler {
* <p>By default, {@link SemanticApiVersionParser} is used to parse configured * <p>By default, {@link SemanticApiVersionParser} is used to parse configured
* API versions, so those can be compared to request versions parsed at runtime. * API versions, so those can be compared to request versions parsed at runtime.
* If you have a custom parser, then please use the * If you have a custom parser, then please use the
* {@link #StandardApiDeprecationHandler(ApiVersionParser)} constructor. * {@link #StandardApiVersionDeprecationHandler(ApiVersionParser)} constructor.
*/ */
public StandardApiDeprecationHandler() { public StandardApiVersionDeprecationHandler() {
this(new SemanticApiVersionParser()); this(new SemanticApiVersionParser());
} }
@ -69,7 +69,7 @@ public class StandardApiDeprecationHandler implements ApiDeprecationHandler {
* This needs to be the same as the parser type used at runtime to parse * This needs to be the same as the parser type used at runtime to parse
* request versions. * request versions.
*/ */
public StandardApiDeprecationHandler(ApiVersionParser<?> parser) { public StandardApiVersionDeprecationHandler(ApiVersionParser<?> parser) {
this.versionParser = parser; this.versionParser = parser;
} }
@ -108,7 +108,7 @@ public class StandardApiDeprecationHandler implements ApiDeprecationHandler {
@Override @Override
public String toString() { public String toString() {
return "StandardApiDeprecationHandler " + this.infos.values(); return "StandardApiVersionDeprecationHandler " + this.infos.values();
} }
@ -121,7 +121,7 @@ public class StandardApiDeprecationHandler implements ApiDeprecationHandler {
private VersionSpec(Comparable<?> version) { private VersionSpec(Comparable<?> version) {
this.version = version; this.version = version;
StandardApiDeprecationHandler.this.infos.put(version, new VersionInfo(version)); StandardApiVersionDeprecationHandler.this.infos.put(version, new VersionInfo(version));
} }
/** /**
@ -197,7 +197,7 @@ public class StandardApiDeprecationHandler implements ApiDeprecationHandler {
} }
private VersionSpec map(Function<VersionInfo, VersionInfo> function) { private VersionSpec map(Function<VersionInfo, VersionInfo> function) {
StandardApiDeprecationHandler.this.infos.compute(this.version, (version, versionInfo) -> { StandardApiVersionDeprecationHandler.this.infos.compute(this.version, (version, versionInfo) -> {
Assert.state(versionInfo != null, "No VersionInfo"); Assert.state(versionInfo != null, "No VersionInfo");
return function.apply(versionInfo); return function.apply(versionInfo);
}); });

View File

@ -29,13 +29,13 @@ import org.springframework.http.MediaType;
import org.springframework.web.accept.ApiVersionParser; import org.springframework.web.accept.ApiVersionParser;
import org.springframework.web.accept.InvalidApiVersionException; import org.springframework.web.accept.InvalidApiVersionException;
import org.springframework.web.accept.SemanticApiVersionParser; import org.springframework.web.accept.SemanticApiVersionParser;
import org.springframework.web.reactive.accept.ApiDeprecationHandler; import org.springframework.web.reactive.accept.ApiVersionDeprecationHandler;
import org.springframework.web.reactive.accept.ApiVersionResolver; import org.springframework.web.reactive.accept.ApiVersionResolver;
import org.springframework.web.reactive.accept.ApiVersionStrategy; import org.springframework.web.reactive.accept.ApiVersionStrategy;
import org.springframework.web.reactive.accept.DefaultApiVersionStrategy; import org.springframework.web.reactive.accept.DefaultApiVersionStrategy;
import org.springframework.web.reactive.accept.MediaTypeParamApiVersionResolver; import org.springframework.web.reactive.accept.MediaTypeParamApiVersionResolver;
import org.springframework.web.reactive.accept.PathApiVersionResolver; import org.springframework.web.reactive.accept.PathApiVersionResolver;
import org.springframework.web.reactive.accept.StandardApiDeprecationHandler; import org.springframework.web.reactive.accept.StandardApiVersionDeprecationHandler;
/** /**
* Configure API versioning. * Configure API versioning.
@ -53,7 +53,7 @@ public class ApiVersionConfigurer {
private @Nullable String defaultVersion; private @Nullable String defaultVersion;
private @Nullable ApiDeprecationHandler deprecationHandler; private @Nullable ApiVersionDeprecationHandler deprecationHandler;
private final Set<String> supportedVersions = new LinkedHashSet<>(); private final Set<String> supportedVersions = new LinkedHashSet<>();
@ -150,9 +150,9 @@ public class ApiVersionConfigurer {
* version. Typically, this involves sending hints and information about * version. Typically, this involves sending hints and information about
* the deprecation in response headers. * the deprecation in response headers.
* @param handler the handler to use * @param handler the handler to use
* @see StandardApiDeprecationHandler * @see StandardApiVersionDeprecationHandler
*/ */
public ApiVersionConfigurer setDeprecationHandler(ApiDeprecationHandler handler) { public ApiVersionConfigurer setDeprecationHandler(ApiVersionDeprecationHandler handler) {
this.deprecationHandler = handler; this.deprecationHandler = handler;
return this; return this;
} }

View File

@ -31,10 +31,10 @@ import org.springframework.web.testfixture.server.MockServerWebExchange;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
/** /**
* Unit tests for {@link StandardApiDeprecationHandler}. * Unit tests for {@link StandardApiVersionDeprecationHandler}.
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
*/ */
public class StandardApiDeprecationHandlerTests { public class StandardApiVersionDeprecationHandlerTests {
private final ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build()); private final ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build());
@ -46,7 +46,7 @@ public class StandardApiDeprecationHandlerTests {
String sunsetUrl = "https://example.org/sunset"; String sunsetUrl = "https://example.org/sunset";
ApiVersionParser<String> parser = version -> version; ApiVersionParser<String> parser = version -> version;
StandardApiDeprecationHandler handler = new StandardApiDeprecationHandler(parser); StandardApiVersionDeprecationHandler handler = new StandardApiVersionDeprecationHandler(parser);
handler.configureVersion("1.1") handler.configureVersion("1.1")
.setDeprecationDate(getDate("Fri, 30 Jun 2023 23:59:59 GMT")) .setDeprecationDate(getDate("Fri, 30 Jun 2023 23:59:59 GMT"))

View File

@ -25,7 +25,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.reactive.accept.StandardApiDeprecationHandler; import org.springframework.web.reactive.accept.StandardApiVersionDeprecationHandler;
import org.springframework.web.reactive.config.ApiVersionConfigurer; import org.springframework.web.reactive.config.ApiVersionConfigurer;
import org.springframework.web.reactive.config.EnableWebFlux; import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer; import org.springframework.web.reactive.config.WebFluxConfigurer;
@ -90,7 +90,7 @@ public class RequestMappingVersionIntegrationTests extends AbstractRequestMappin
@Override @Override
public void configureApiVersioning(ApiVersionConfigurer configurer) { public void configureApiVersioning(ApiVersionConfigurer configurer) {
StandardApiDeprecationHandler handler = new StandardApiDeprecationHandler(); StandardApiVersionDeprecationHandler handler = new StandardApiVersionDeprecationHandler();
handler.configureVersion("1").setDeprecationLink(URI.create("https://example.org/deprecation")); handler.configureVersion("1").setDeprecationLink(URI.create("https://example.org/deprecation"));
configurer.useRequestHeader("X-API-Version") configurer.useRequestHeader("X-API-Version")

View File

@ -26,7 +26,7 @@ import java.util.Set;
import org.jspecify.annotations.Nullable; import org.jspecify.annotations.Nullable;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.accept.ApiDeprecationHandler; import org.springframework.web.accept.ApiVersionDeprecationHandler;
import org.springframework.web.accept.ApiVersionParser; import org.springframework.web.accept.ApiVersionParser;
import org.springframework.web.accept.ApiVersionResolver; import org.springframework.web.accept.ApiVersionResolver;
import org.springframework.web.accept.ApiVersionStrategy; import org.springframework.web.accept.ApiVersionStrategy;
@ -34,7 +34,7 @@ import org.springframework.web.accept.DefaultApiVersionStrategy;
import org.springframework.web.accept.MediaTypeParamApiVersionResolver; import org.springframework.web.accept.MediaTypeParamApiVersionResolver;
import org.springframework.web.accept.PathApiVersionResolver; import org.springframework.web.accept.PathApiVersionResolver;
import org.springframework.web.accept.SemanticApiVersionParser; import org.springframework.web.accept.SemanticApiVersionParser;
import org.springframework.web.accept.StandardApiDeprecationHandler; import org.springframework.web.accept.StandardApiVersionDeprecationHandler;
/** /**
* Configure API versioning. * Configure API versioning.
@ -52,7 +52,7 @@ public class ApiVersionConfigurer {
private @Nullable String defaultVersion; private @Nullable String defaultVersion;
private @Nullable ApiDeprecationHandler deprecationHandler; private @Nullable ApiVersionDeprecationHandler deprecationHandler;
private final Set<String> supportedVersions = new LinkedHashSet<>(); private final Set<String> supportedVersions = new LinkedHashSet<>();
@ -147,9 +147,9 @@ public class ApiVersionConfigurer {
* version. Typically, this involves sending hints and information about * version. Typically, this involves sending hints and information about
* the deprecation in response headers. * the deprecation in response headers.
* @param handler the handler to use * @param handler the handler to use
* @see StandardApiDeprecationHandler * @see StandardApiVersionDeprecationHandler
*/ */
public ApiVersionConfigurer setDeprecationHandler(ApiDeprecationHandler handler) { public ApiVersionConfigurer setDeprecationHandler(ApiVersionDeprecationHandler handler) {
this.deprecationHandler = handler; this.deprecationHandler = handler;
return this; return this;
} }

View File

@ -23,7 +23,7 @@ import jakarta.servlet.ServletException;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.web.accept.StandardApiDeprecationHandler; import org.springframework.web.accept.StandardApiVersionDeprecationHandler;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
@ -99,7 +99,7 @@ public class RequestMappingVersionHandlerMethodTests {
@Override @Override
public void configureApiVersioning(ApiVersionConfigurer configurer) { public void configureApiVersioning(ApiVersionConfigurer configurer) {
StandardApiDeprecationHandler handler = new StandardApiDeprecationHandler(); StandardApiVersionDeprecationHandler handler = new StandardApiVersionDeprecationHandler();
handler.configureVersion("1").setDeprecationLink(URI.create("https://example.org/deprecation")); handler.configureVersion("1").setDeprecationLink(URI.create("https://example.org/deprecation"));
configurer.useRequestHeader("X-API-Version") configurer.useRequestHeader("X-API-Version")