Skip to content

Commit 86f50b2

Browse files
committed
Move ApiVersionStrategy up to AbstractHandlerMapping
Similar to CorProcessor, ApiVersionStrategy is now supported at the AbstractHandlerMapping level. See gh-35113
1 parent 93a9c9b commit 86f50b2

File tree

6 files changed

+98
-169
lines changed

6 files changed

+98
-169
lines changed

spring-webflux/src/main/java/org/springframework/web/reactive/function/server/support/RouterFunctionMapping.java

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import org.springframework.http.codec.ServerCodecConfigurer;
3030
import org.springframework.http.server.reactive.observation.ServerRequestObservationContext;
3131
import org.springframework.util.CollectionUtils;
32-
import org.springframework.web.reactive.accept.ApiVersionStrategy;
3332
import org.springframework.web.reactive.accept.DefaultApiVersionStrategy;
3433
import org.springframework.web.reactive.function.server.HandlerFunction;
3534
import org.springframework.web.reactive.function.server.RouterFunction;
@@ -56,8 +55,6 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
5655

5756
private List<HttpMessageReader<?>> messageReaders = Collections.emptyList();
5857

59-
private @Nullable ApiVersionStrategy versionStrategy;
60-
6158

6259
/**
6360
* Create an empty {@code RouterFunctionMapping}.
@@ -96,15 +93,6 @@ public void setMessageReaders(List<HttpMessageReader<?>> messageReaders) {
9693
this.messageReaders = messageReaders;
9794
}
9895

99-
/**
100-
* Configure a strategy to manage API versioning.
101-
* @param strategy the strategy to use
102-
* @since 7.0
103-
*/
104-
public void setApiVersionStrategy(@Nullable ApiVersionStrategy strategy) {
105-
this.versionStrategy = strategy;
106-
}
107-
10896

10997
@Override
11098
public void afterPropertiesSet() throws Exception {
@@ -119,7 +107,7 @@ public void afterPropertiesSet() throws Exception {
119107

120108
if (this.routerFunction != null) {
121109
RouterFunctions.changeParser(this.routerFunction, getPathPatternParser());
122-
if (this.versionStrategy instanceof DefaultApiVersionStrategy davs) {
110+
if (getApiVersionStrategy() instanceof DefaultApiVersionStrategy davs) {
123111
if (davs.detectSupportedVersions()) {
124112
this.routerFunction.accept(new SupportedVersionVisitor(davs));
125113
}
@@ -169,24 +157,10 @@ else if (total > 0) {
169157

170158
@Override
171159
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
172-
173160
if (this.routerFunction == null) {
174161
return Mono.empty();
175162
}
176-
177-
if (this.versionStrategy != null) {
178-
Comparable<?> version = exchange.getAttribute(API_VERSION_ATTRIBUTE);
179-
if (version == null) {
180-
version = this.versionStrategy.resolveParseAndValidateVersion(exchange);
181-
if (version != null) {
182-
exchange.getAttributes().put(API_VERSION_ATTRIBUTE, version);
183-
}
184-
}
185-
}
186-
187-
ServerRequest request = ServerRequest.create(
188-
exchange, this.messageReaders, this.versionStrategy);
189-
163+
ServerRequest request = ServerRequest.create(exchange, this.messageReaders, getApiVersionStrategy());
190164
return this.routerFunction.route(request)
191165
.doOnNext(handler -> setAttributes(exchange.getAttributes(), request, handler));
192166
}

spring-webflux/src/main/java/org/springframework/web/reactive/handler/AbstractHandlerMapping.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.web.cors.reactive.DefaultCorsProcessor;
3636
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
3737
import org.springframework.web.reactive.HandlerMapping;
38+
import org.springframework.web.reactive.accept.ApiVersionStrategy;
3839
import org.springframework.web.server.ServerWebExchange;
3940
import org.springframework.web.server.WebHandler;
4041
import org.springframework.web.util.pattern.PathPatternParser;
@@ -64,6 +65,8 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport
6465

6566
private CorsProcessor corsProcessor = new DefaultCorsProcessor();
6667

68+
private @Nullable ApiVersionStrategy apiVersionStrategy;
69+
6770
private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered
6871

6972
private @Nullable String beanName;
@@ -138,6 +141,23 @@ public CorsProcessor getCorsProcessor() {
138141
return this.corsProcessor;
139142
}
140143

144+
/**
145+
* Configure a strategy to manage API versioning.
146+
* @param strategy the strategy to use
147+
* @since 7.0
148+
*/
149+
public void setApiVersionStrategy(@Nullable ApiVersionStrategy strategy) {
150+
this.apiVersionStrategy = strategy;
151+
}
152+
153+
/**
154+
* Return the configured {@link ApiVersionStrategy} strategy.
155+
* @since 7.0
156+
*/
157+
public @Nullable ApiVersionStrategy getApiVersionStrategy() {
158+
return this.apiVersionStrategy;
159+
}
160+
141161
/**
142162
* Specify the order value for this HandlerMapping bean.
143163
* <p>The default value is {@code Ordered.LOWEST_PRECEDENCE}, meaning non-ordered.
@@ -164,6 +184,7 @@ protected String formatMappingName() {
164184

165185
@Override
166186
public Mono<Object> getHandler(ServerWebExchange exchange) {
187+
initApiVersion(exchange);
167188
return getHandlerInternal(exchange).map(handler -> {
168189
if (logger.isDebugEnabled()) {
169190
logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);
@@ -182,10 +203,28 @@ public Mono<Object> getHandler(ServerWebExchange exchange) {
182203
return NO_OP_HANDLER;
183204
}
184205
}
206+
if (getApiVersionStrategy() != null) {
207+
Comparable<?> version = exchange.getAttribute(API_VERSION_ATTRIBUTE);
208+
if (version != null) {
209+
getApiVersionStrategy().handleDeprecations(version, exchange);
210+
}
211+
}
185212
return handler;
186213
});
187214
}
188215

216+
private void initApiVersion(ServerWebExchange exchange) {
217+
if (this.apiVersionStrategy != null) {
218+
Comparable<?> version = exchange.getAttribute(API_VERSION_ATTRIBUTE);
219+
if (version == null) {
220+
version = this.apiVersionStrategy.resolveParseAndValidateVersion(exchange);
221+
if (version != null) {
222+
exchange.getAttributes().put(API_VERSION_ATTRIBUTE, version);
223+
}
224+
}
225+
}
226+
}
227+
189228
/**
190229
* Look up a handler for the given request, returning an empty {@code Mono}
191230
* if no specific one is found. This method is called by {@link #getHandler}.

spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import java.util.stream.Stream;
2929

3030
import org.jspecify.annotations.Nullable;
31-
import reactor.core.publisher.Mono;
3231

3332
import org.springframework.context.EmbeddedValueResolverAware;
3433
import org.springframework.core.annotation.AnnotatedElementUtils;
@@ -48,15 +47,13 @@
4847
import org.springframework.web.bind.annotation.RequestMethod;
4948
import org.springframework.web.cors.CorsConfiguration;
5049
import org.springframework.web.method.HandlerMethod;
51-
import org.springframework.web.reactive.accept.ApiVersionStrategy;
5250
import org.springframework.web.reactive.accept.DefaultApiVersionStrategy;
5351
import org.springframework.web.reactive.accept.RequestedContentTypeResolver;
5452
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder;
5553
import org.springframework.web.reactive.result.condition.ConsumesRequestCondition;
5654
import org.springframework.web.reactive.result.condition.RequestCondition;
5755
import org.springframework.web.reactive.result.method.RequestMappingInfo;
5856
import org.springframework.web.reactive.result.method.RequestMappingInfoHandlerMapping;
59-
import org.springframework.web.server.ServerWebExchange;
6057
import org.springframework.web.service.annotation.HttpExchange;
6158

6259
/**
@@ -82,8 +79,6 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
8279

8380
private RequestedContentTypeResolver contentTypeResolver = new RequestedContentTypeResolverBuilder().build();
8481

85-
private @Nullable ApiVersionStrategy apiVersionStrategy;
86-
8782
private @Nullable StringValueResolver embeddedValueResolver;
8883

8984
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
@@ -132,23 +127,6 @@ public RequestedContentTypeResolver getContentTypeResolver() {
132127
return this.contentTypeResolver;
133128
}
134129

135-
/**
136-
* Configure a strategy to manage API versioning.
137-
* @param strategy the strategy to use
138-
* @since 7.0
139-
*/
140-
public void setApiVersionStrategy(@Nullable ApiVersionStrategy strategy) {
141-
this.apiVersionStrategy = strategy;
142-
}
143-
144-
/**
145-
* Return the configured {@link ApiVersionStrategy} strategy.
146-
* @since 7.0
147-
*/
148-
public @Nullable ApiVersionStrategy getApiVersionStrategy() {
149-
return this.apiVersionStrategy;
150-
}
151-
152130
@Override
153131
public void setEmbeddedValueResolver(StringValueResolver resolver) {
154132
this.embeddedValueResolver = resolver;
@@ -174,20 +152,6 @@ protected boolean isHandler(Class<?> beanType) {
174152
return AnnotatedElementUtils.hasAnnotation(beanType, Controller.class);
175153
}
176154

177-
@Override
178-
public Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange) {
179-
if (this.apiVersionStrategy != null) {
180-
Comparable<?> version = exchange.getAttribute(API_VERSION_ATTRIBUTE);
181-
if (version == null) {
182-
version = this.apiVersionStrategy.resolveParseAndValidateVersion(exchange);
183-
if (version != null) {
184-
exchange.getAttributes().put(API_VERSION_ATTRIBUTE, version);
185-
}
186-
}
187-
}
188-
return super.getHandlerInternal(exchange);
189-
}
190-
191155
/**
192156
* Uses type-level and method-level {@link RequestMapping @RequestMapping}
193157
* and {@link HttpExchange @HttpExchange} annotations to create the
@@ -253,7 +217,7 @@ public Mono<HandlerMethod> getHandlerInternal(ServerWebExchange exchange) {
253217
info = createRequestMappingInfo((HttpExchange) exchangeDescriptors.get(0).annotation, customCondition);
254218
}
255219

256-
if (info != null && this.apiVersionStrategy instanceof DefaultApiVersionStrategy davs) {
220+
if (info != null && getApiVersionStrategy() instanceof DefaultApiVersionStrategy davs) {
257221
String version = info.getVersionCondition().getVersion();
258222
if (version != null) {
259223
davs.addMappedVersion(version);
@@ -397,17 +361,6 @@ private static RequestMethod[] toMethodArray(String method) {
397361
new RequestMethod[] {RequestMethod.valueOf(method)} : EMPTY_REQUEST_METHOD_ARRAY);
398362
}
399363

400-
@Override
401-
protected void handleMatch(RequestMappingInfo info, HandlerMethod handlerMethod, ServerWebExchange exchange) {
402-
super.handleMatch(info, handlerMethod, exchange);
403-
404-
Comparable<?> version = exchange.getAttribute(API_VERSION_ATTRIBUTE);
405-
if (version != null) {
406-
Assert.state(this.apiVersionStrategy != null, "No ApiVersionStrategy");
407-
this.apiVersionStrategy.handleDeprecations(version, exchange);
408-
}
409-
}
410-
411364
@Override
412365
public void registerMapping(RequestMappingInfo mapping, Object handler, Method method) {
413366
super.registerMapping(mapping, handler, method);

spring-webmvc/src/main/java/org/springframework/web/servlet/function/support/RouterFunctionMapping.java

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import org.springframework.http.converter.StringHttpMessageConverter;
3232
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
3333
import org.springframework.util.CollectionUtils;
34-
import org.springframework.web.accept.ApiVersionStrategy;
3534
import org.springframework.web.accept.DefaultApiVersionStrategy;
3635
import org.springframework.web.filter.ServerHttpObservationFilter;
3736
import org.springframework.web.servlet.function.HandlerFunction;
@@ -64,8 +63,6 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
6463

6564
private List<HttpMessageConverter<?>> messageConverters = Collections.emptyList();
6665

67-
private @Nullable ApiVersionStrategy versionStrategy;
68-
6966
private boolean detectHandlerFunctionsInAncestorContexts = false;
7067

7168

@@ -114,15 +111,6 @@ public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters
114111
this.messageConverters = messageConverters;
115112
}
116113

117-
/**
118-
* Configure a strategy to manage API versioning.
119-
* @param strategy the strategy to use
120-
* @since 7.0
121-
*/
122-
public void setApiVersionStrategy(@Nullable ApiVersionStrategy strategy) {
123-
this.versionStrategy = strategy;
124-
}
125-
126114
/**
127115
* Set whether to detect handler functions in ancestor ApplicationContexts.
128116
* <p>Default is "false": Only handler functions in the current ApplicationContext
@@ -154,7 +142,7 @@ public void afterPropertiesSet() {
154142
}
155143
RouterFunctions.changeParser(this.routerFunction, patternParser);
156144

157-
if (this.versionStrategy instanceof DefaultApiVersionStrategy davs) {
145+
if (getApiVersionStrategy() instanceof DefaultApiVersionStrategy davs) {
158146
if (davs.detectSupportedVersions()) {
159147
this.routerFunction.accept(new SupportedVersionVisitor(davs));
160148
}
@@ -218,24 +206,10 @@ private void initMessageConverters() {
218206

219207
@Override
220208
protected @Nullable Object getHandlerInternal(HttpServletRequest servletRequest) throws Exception {
221-
222209
if (this.routerFunction == null) {
223210
return null;
224211
}
225-
226-
if (this.versionStrategy != null) {
227-
Comparable<?> version = (Comparable<?>) servletRequest.getAttribute(API_VERSION_ATTRIBUTE);
228-
if (version == null) {
229-
version = this.versionStrategy.resolveParseAndValidateVersion(servletRequest);
230-
if (version != null) {
231-
servletRequest.setAttribute(API_VERSION_ATTRIBUTE, version);
232-
}
233-
}
234-
}
235-
236-
ServerRequest request =
237-
ServerRequest.create(servletRequest, this.messageConverters, this.versionStrategy);
238-
212+
ServerRequest request = ServerRequest.create(servletRequest, this.messageConverters, getApiVersionStrategy());
239213
HandlerFunction<?> handlerFunction = this.routerFunction.route(request).orElse(null);
240214
setAttributes(servletRequest, request, handlerFunction);
241215
return handlerFunction;

0 commit comments

Comments
 (0)