RouterFunction honors PathPatternParser in config
This commit introduces a way to change the PathPatternParser used in PathPredicates, by way of a ChangePathPatternParserVisitor. This visitor is used by both WebFluxConfigurationSupport and WebMvcConfigurationSupport to make sure the configured parser is used. Closes gh-23236
This commit is contained in:
parent
671d4519b2
commit
e9d9de5f99
|
@ -128,9 +128,18 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
|||
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
|
||||
mapping.setOrder(0);
|
||||
mapping.setContentTypeResolver(contentTypeResolver);
|
||||
mapping.setCorsConfigurations(getCorsConfigurations());
|
||||
|
||||
PathMatchConfigurer configurer = getPathMatchConfigurer();
|
||||
configureAbstractHandlerMapping(mapping, configurer);
|
||||
Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes();
|
||||
if (pathPrefixes != null) {
|
||||
mapping.setPathPrefixes(pathPrefixes);
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
private void configureAbstractHandlerMapping(AbstractHandlerMapping mapping, PathMatchConfigurer configurer) {
|
||||
mapping.setCorsConfigurations(getCorsConfigurations());
|
||||
Boolean useTrailingSlashMatch = configurer.isUseTrailingSlashMatch();
|
||||
if (useTrailingSlashMatch != null) {
|
||||
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
|
||||
|
@ -139,12 +148,6 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
|||
if (useCaseSensitiveMatch != null) {
|
||||
mapping.setUseCaseSensitiveMatch(useCaseSensitiveMatch);
|
||||
}
|
||||
Map<String, Predicate<Class<?>>> pathPrefixes = configurer.getPathPrefixes();
|
||||
if (pathPrefixes != null) {
|
||||
mapping.setPathPrefixes(pathPrefixes);
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -210,8 +213,7 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
|||
RouterFunctionMapping mapping = createRouterFunctionMapping();
|
||||
mapping.setOrder(-1); // go before RequestMappingHandlerMapping
|
||||
mapping.setMessageReaders(serverCodecConfigurer.getReaders());
|
||||
mapping.setCorsConfigurations(getCorsConfigurations());
|
||||
|
||||
configureAbstractHandlerMapping(mapping, getPathMatchConfigurer());
|
||||
return mapping;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright 2002-2020 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.function.server;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
/**
|
||||
* Implementation of {@link RouterFunctions.Visitor} that changes the
|
||||
* {@link PathPatternParser} on path-related request predicates
|
||||
* (i.e. {@code RequestPredicates.PathPatternPredicate}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.3
|
||||
*/
|
||||
class ChangePathPatternParserVisitor implements RouterFunctions.Visitor {
|
||||
|
||||
private final PathPatternParser parser;
|
||||
|
||||
|
||||
public ChangePathPatternParserVisitor(PathPatternParser parser) {
|
||||
Assert.notNull(parser, "Parser must not be null");
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startNested(RequestPredicate predicate) {
|
||||
changeParser(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endNested(RequestPredicate predicate) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void route(RequestPredicate predicate, HandlerFunction<?> handlerFunction) {
|
||||
changeParser(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resources(Function<ServerRequest, Mono<Resource>> lookupFunction) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unknown(RouterFunction<?> routerFunction) {
|
||||
}
|
||||
|
||||
private void changeParser(RequestPredicate predicate) {
|
||||
if (predicate instanceof Target) {
|
||||
Target target = (Target) predicate;
|
||||
target.changeParser(this.parser);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interface implemented by predicates that can change the parser.
|
||||
*/
|
||||
public interface Target {
|
||||
|
||||
void changeParser(PathPatternParser parser);
|
||||
}
|
||||
}
|
|
@ -441,7 +441,6 @@ public abstract class RequestPredicates {
|
|||
|
||||
public HttpMethodPredicate(HttpMethod... httpMethods) {
|
||||
Assert.notEmpty(httpMethods, "HttpMethods must not be empty");
|
||||
|
||||
this.httpMethods = EnumSet.copyOf(Arrays.asList(httpMethods));
|
||||
}
|
||||
|
||||
|
@ -465,7 +464,6 @@ public abstract class RequestPredicates {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void accept(Visitor visitor) {
|
||||
visitor.method(Collections.unmodifiableSet(this.httpMethods));
|
||||
|
@ -483,9 +481,9 @@ public abstract class RequestPredicates {
|
|||
}
|
||||
|
||||
|
||||
private static class PathPatternPredicate implements RequestPredicate {
|
||||
private static class PathPatternPredicate implements RequestPredicate, ChangePathPatternParserVisitor.Target {
|
||||
|
||||
private final PathPattern pattern;
|
||||
private PathPattern pattern;
|
||||
|
||||
public PathPatternPredicate(PathPattern pattern) {
|
||||
Assert.notNull(pattern, "'pattern' must not be null");
|
||||
|
@ -529,10 +527,17 @@ public abstract class RequestPredicates {
|
|||
visitor.path(this.pattern.getPatternString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeParser(PathPatternParser parser) {
|
||||
String patternString = this.pattern.getPatternString();
|
||||
this.pattern = parser.parse(patternString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.pattern.getPatternString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -750,7 +755,7 @@ public abstract class RequestPredicates {
|
|||
* {@link RequestPredicate} for where both {@code left} and {@code right} predicates
|
||||
* must match.
|
||||
*/
|
||||
static class AndRequestPredicate implements RequestPredicate {
|
||||
static class AndRequestPredicate implements RequestPredicate, ChangePathPatternParserVisitor.Target {
|
||||
|
||||
private final RequestPredicate left;
|
||||
|
||||
|
@ -788,6 +793,16 @@ public abstract class RequestPredicates {
|
|||
visitor.endAnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeParser(PathPatternParser parser) {
|
||||
if (this.left instanceof ChangePathPatternParserVisitor.Target) {
|
||||
((ChangePathPatternParserVisitor.Target) left).changeParser(parser);
|
||||
}
|
||||
if (this.right instanceof ChangePathPatternParserVisitor.Target) {
|
||||
((ChangePathPatternParserVisitor.Target) right).changeParser(parser);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("(%s && %s)", this.left, this.right);
|
||||
|
@ -797,7 +812,8 @@ public abstract class RequestPredicates {
|
|||
/**
|
||||
* {@link RequestPredicate} that negates a delegate predicate.
|
||||
*/
|
||||
static class NegateRequestPredicate implements RequestPredicate {
|
||||
static class NegateRequestPredicate implements RequestPredicate, ChangePathPatternParserVisitor.Target {
|
||||
|
||||
private final RequestPredicate delegate;
|
||||
|
||||
public NegateRequestPredicate(RequestPredicate delegate) {
|
||||
|
@ -822,6 +838,13 @@ public abstract class RequestPredicates {
|
|||
visitor.endNegate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeParser(PathPatternParser parser) {
|
||||
if (this.delegate instanceof ChangePathPatternParserVisitor.Target) {
|
||||
((ChangePathPatternParserVisitor.Target) delegate).changeParser(parser);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "!" + this.delegate.toString();
|
||||
|
@ -832,7 +855,7 @@ public abstract class RequestPredicates {
|
|||
* {@link RequestPredicate} where either {@code left} or {@code right} predicates
|
||||
* may match.
|
||||
*/
|
||||
static class OrRequestPredicate implements RequestPredicate {
|
||||
static class OrRequestPredicate implements RequestPredicate, ChangePathPatternParserVisitor.Target {
|
||||
|
||||
private final RequestPredicate left;
|
||||
|
||||
|
@ -882,6 +905,15 @@ public abstract class RequestPredicates {
|
|||
visitor.endOr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeParser(PathPatternParser parser) {
|
||||
if (this.left instanceof ChangePathPatternParserVisitor.Target) {
|
||||
((ChangePathPatternParserVisitor.Target) left).changeParser(parser);
|
||||
}
|
||||
if (this.right instanceof ChangePathPatternParserVisitor.Target) {
|
||||
((ChangePathPatternParserVisitor.Target) right).changeParser(parser);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.springframework.web.reactive.result.view.ViewResolver;
|
|||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
/**
|
||||
* <strong>Central entry point to Spring's functional web framework.</strong>
|
||||
|
@ -255,6 +256,26 @@ public abstract class RouterFunctions {
|
|||
return new RouterFunctionWebHandler(strategies, routerFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the {@link PathPatternParser} on the given {@linkplain RouterFunction router function}. This method
|
||||
* can be used to change the {@code PathPatternParser} properties from the defaults, for instance to change
|
||||
* {@linkplain PathPatternParser#setCaseSensitive(boolean) case sensitivity}.
|
||||
* @param routerFunction the router function to change the parser in
|
||||
* @param parser the parser to change to.
|
||||
* @param <T> the type of response returned by the handler function
|
||||
* @return the change router function
|
||||
*/
|
||||
public static <T extends ServerResponse> RouterFunction<T> changeParser(RouterFunction<T> routerFunction,
|
||||
PathPatternParser parser) {
|
||||
|
||||
Assert.notNull(routerFunction, "RouterFunction must not be null");
|
||||
Assert.notNull(parser, "Parser must not be null");
|
||||
|
||||
ChangePathPatternParserVisitor visitor = new ChangePathPatternParserVisitor(parser);
|
||||
routerFunction.accept(visitor);
|
||||
return routerFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a discoverable builder for router functions.
|
||||
* Obtained via {@link RouterFunctions#route()}.
|
||||
|
@ -895,6 +916,7 @@ public abstract class RouterFunctions {
|
|||
public void accept(Visitor visitor) {
|
||||
visitor.route(this.predicate, this.handlerFunction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -938,6 +960,7 @@ public abstract class RouterFunctions {
|
|||
this.routerFunction.accept(visitor);
|
||||
visitor.endNested(this.predicate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
|
@ -103,6 +103,10 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
|
|||
if (this.routerFunction == null) {
|
||||
initRouterFunctions();
|
||||
}
|
||||
if (this.routerFunction != null) {
|
||||
RouterFunctions.changeParser(this.routerFunction, getPathPatternParser());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
|
@ -23,6 +23,7 @@ import reactor.test.StepVerifier;
|
|||
import org.springframework.http.codec.ServerCodecConfigurer;
|
||||
import org.springframework.web.reactive.function.server.HandlerFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunction;
|
||||
import org.springframework.web.reactive.function.server.RouterFunctions;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest;
|
||||
|
@ -67,4 +68,26 @@ public class RouterFunctionMappingTests {
|
|||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeParser() throws Exception {
|
||||
HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.ok().build();
|
||||
RouterFunction<ServerResponse> routerFunction = RouterFunctions.route()
|
||||
.GET("/foo", handlerFunction)
|
||||
.POST("/bar", handlerFunction)
|
||||
.build();
|
||||
|
||||
RouterFunctionMapping mapping = new RouterFunctionMapping(routerFunction);
|
||||
mapping.setMessageReaders(this.codecConfigurer.getReaders());
|
||||
mapping.setUseCaseSensitiveMatch(false);
|
||||
mapping.afterPropertiesSet();
|
||||
|
||||
ServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("https://example.com/FOO"));
|
||||
Mono<Object> result = mapping.getHandler(exchange);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext(handlerFunction)
|
||||
.verifyComplete();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -109,6 +109,7 @@ import org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator;
|
|||
import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||
import org.springframework.web.servlet.view.ViewResolverComposite;
|
||||
import org.springframework.web.util.UrlPathHelper;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
/**
|
||||
* This is the main class providing the configuration behind the MVC Java config.
|
||||
|
@ -519,6 +520,7 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
|||
* <li>{@link #addInterceptors} for adding handler interceptors.
|
||||
* <li>{@link #addCorsMappings} to configure cross origin requests processing.
|
||||
* <li>{@link #configureMessageConverters} for adding custom message converters.
|
||||
* <li>{@link #configurePathMatch(PathMatchConfigurer)} for customizing the {@link PathPatternParser}.
|
||||
* </ul>
|
||||
* @since 5.2
|
||||
*/
|
||||
|
@ -532,6 +534,12 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
|||
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
|
||||
mapping.setCorsConfigurations(getCorsConfigurations());
|
||||
mapping.setMessageConverters(getMessageConverters());
|
||||
|
||||
PathPatternParser patternParser = getPathMatchConfigurer().getPatternParser();
|
||||
if (patternParser != null) {
|
||||
mapping.setPatternParser(patternParser);
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2002-2020 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.servlet.function;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
/**
|
||||
* Implementation of {@link RouterFunctions.Visitor} that changes the
|
||||
* {@link PathPatternParser} on path-related request predicates
|
||||
* (i.e. {@code RequestPredicates.PathPatternPredicate}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @since 5.3
|
||||
*/
|
||||
class ChangePathPatternParserVisitor implements RouterFunctions.Visitor {
|
||||
|
||||
private final PathPatternParser parser;
|
||||
|
||||
|
||||
public ChangePathPatternParserVisitor(PathPatternParser parser) {
|
||||
Assert.notNull(parser, "Parser must not be null");
|
||||
this.parser = parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startNested(RequestPredicate predicate) {
|
||||
changeParser(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endNested(RequestPredicate predicate) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void route(RequestPredicate predicate, HandlerFunction<?> handlerFunction) {
|
||||
changeParser(predicate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resources(Function<ServerRequest, Optional<Resource>> lookupFunction) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unknown(RouterFunction<?> routerFunction) {
|
||||
}
|
||||
|
||||
private void changeParser(RequestPredicate predicate) {
|
||||
if (predicate instanceof Target) {
|
||||
Target target = (Target) predicate;
|
||||
target.changeParser(this.parser);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interface implemented by predicates that can change the parser.
|
||||
*/
|
||||
public interface Target {
|
||||
|
||||
void changeParser(PathPatternParser parser);
|
||||
}
|
||||
}
|
|
@ -427,11 +427,11 @@ public abstract class RequestPredicates {
|
|||
void unknown(RequestPredicate predicate);
|
||||
}
|
||||
|
||||
|
||||
private static class HttpMethodPredicate implements RequestPredicate {
|
||||
|
||||
private final Set<HttpMethod> httpMethods;
|
||||
|
||||
|
||||
public HttpMethodPredicate(HttpMethod httpMethod) {
|
||||
Assert.notNull(httpMethod, "HttpMethod must not be null");
|
||||
this.httpMethods = EnumSet.of(httpMethod);
|
||||
|
@ -479,9 +479,9 @@ public abstract class RequestPredicates {
|
|||
}
|
||||
|
||||
|
||||
private static class PathPatternPredicate implements RequestPredicate {
|
||||
private static class PathPatternPredicate implements RequestPredicate, ChangePathPatternParserVisitor.Target {
|
||||
|
||||
private final PathPattern pattern;
|
||||
private PathPattern pattern;
|
||||
|
||||
public PathPatternPredicate(PathPattern pattern) {
|
||||
Assert.notNull(pattern, "'pattern' must not be null");
|
||||
|
@ -513,6 +513,7 @@ public abstract class RequestPredicates {
|
|||
pattern);
|
||||
request.attributes().put(RouterFunctions.MATCHING_PATTERN_ATTRIBUTE, pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ServerRequest> nest(ServerRequest request) {
|
||||
return Optional.ofNullable(this.pattern.matchStartOfPath(request.pathContainer()))
|
||||
|
@ -524,10 +525,17 @@ public abstract class RequestPredicates {
|
|||
visitor.path(this.pattern.getPatternString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeParser(PathPatternParser parser) {
|
||||
String patternString = this.pattern.getPatternString();
|
||||
this.pattern = parser.parse(patternString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.pattern.getPatternString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -642,12 +650,14 @@ public abstract class RequestPredicates {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private static class PathExtensionPredicate implements RequestPredicate {
|
||||
|
||||
private final Predicate<String> extensionPredicate;
|
||||
|
||||
@Nullable
|
||||
private final String extension;
|
||||
|
||||
public PathExtensionPredicate(Predicate<String> extensionPredicate) {
|
||||
Assert.notNull(extensionPredicate, "Predicate must not be null");
|
||||
this.extensionPredicate = extensionPredicate;
|
||||
|
@ -743,7 +753,7 @@ public abstract class RequestPredicates {
|
|||
* {@link RequestPredicate} for where both {@code left} and {@code right} predicates
|
||||
* must match.
|
||||
*/
|
||||
static class AndRequestPredicate implements RequestPredicate {
|
||||
static class AndRequestPredicate implements RequestPredicate, ChangePathPatternParserVisitor.Target {
|
||||
|
||||
private final RequestPredicate left;
|
||||
|
||||
|
@ -781,6 +791,16 @@ public abstract class RequestPredicates {
|
|||
visitor.endAnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeParser(PathPatternParser parser) {
|
||||
if (this.left instanceof ChangePathPatternParserVisitor.Target) {
|
||||
((ChangePathPatternParserVisitor.Target) left).changeParser(parser);
|
||||
}
|
||||
if (this.right instanceof ChangePathPatternParserVisitor.Target) {
|
||||
((ChangePathPatternParserVisitor.Target) right).changeParser(parser);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("(%s && %s)", this.left, this.right);
|
||||
|
@ -790,7 +810,8 @@ public abstract class RequestPredicates {
|
|||
/**
|
||||
* {@link RequestPredicate} that negates a delegate predicate.
|
||||
*/
|
||||
static class NegateRequestPredicate implements RequestPredicate {
|
||||
static class NegateRequestPredicate implements RequestPredicate, ChangePathPatternParserVisitor.Target {
|
||||
|
||||
private final RequestPredicate delegate;
|
||||
|
||||
public NegateRequestPredicate(RequestPredicate delegate) {
|
||||
|
@ -815,6 +836,13 @@ public abstract class RequestPredicates {
|
|||
visitor.endNegate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeParser(PathPatternParser parser) {
|
||||
if (this.delegate instanceof ChangePathPatternParserVisitor.Target) {
|
||||
((ChangePathPatternParserVisitor.Target) delegate).changeParser(parser);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "!" + this.delegate.toString();
|
||||
|
@ -825,7 +853,7 @@ public abstract class RequestPredicates {
|
|||
* {@link RequestPredicate} where either {@code left} or {@code right} predicates
|
||||
* may match.
|
||||
*/
|
||||
static class OrRequestPredicate implements RequestPredicate {
|
||||
static class OrRequestPredicate implements RequestPredicate, ChangePathPatternParserVisitor.Target {
|
||||
|
||||
private final RequestPredicate left;
|
||||
|
||||
|
@ -875,6 +903,16 @@ public abstract class RequestPredicates {
|
|||
visitor.endOr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeParser(PathPatternParser parser) {
|
||||
if (this.left instanceof ChangePathPatternParserVisitor.Target) {
|
||||
((ChangePathPatternParserVisitor.Target) left).changeParser(parser);
|
||||
}
|
||||
if (this.right instanceof ChangePathPatternParserVisitor.Target) {
|
||||
((ChangePathPatternParserVisitor.Target) right).changeParser(parser);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("(%s || %s)", this.left, this.right);
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
/**
|
||||
* <strong>Central entry point to Spring's functional web framework.</strong>
|
||||
|
@ -168,6 +169,26 @@ public abstract class RouterFunctions {
|
|||
return new ResourcesRouterFunction(lookupFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the {@link PathPatternParser} on the given {@linkplain RouterFunction router function}. This method
|
||||
* can be used to change the {@code PathPatternParser} properties from the defaults, for instance to change
|
||||
* {@linkplain PathPatternParser#setCaseSensitive(boolean) case sensitivity}.
|
||||
* @param routerFunction the router function to change the parser in
|
||||
* @param parser the parser to change to.
|
||||
* @param <T> the type of response returned by the handler function
|
||||
* @return the change router function
|
||||
*/
|
||||
public static <T extends ServerResponse> RouterFunction<T> changeParser(RouterFunction<T> routerFunction,
|
||||
PathPatternParser parser) {
|
||||
|
||||
Assert.notNull(routerFunction, "RouterFunction must not be null");
|
||||
Assert.notNull(parser, "Parser must not be null");
|
||||
|
||||
ChangePathPatternParserVisitor visitor = new ChangePathPatternParserVisitor(parser);
|
||||
routerFunction.accept(visitor);
|
||||
return routerFunction;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a discoverable builder for router functions.
|
||||
|
@ -617,6 +638,8 @@ public abstract class RouterFunctions {
|
|||
*/
|
||||
RouterFunction<ServerResponse> build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Receives notifications from the logical structure of router functions.
|
||||
*/
|
||||
|
@ -670,6 +693,7 @@ public abstract class RouterFunctions {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A composed routing function that first invokes one function, and then invokes the
|
||||
* another function (of the same response type {@code T}) if this route had
|
||||
|
@ -705,6 +729,7 @@ public abstract class RouterFunctions {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A composed routing function that first invokes one function, and then invokes
|
||||
* another function (of a different response type) if this route had
|
||||
|
@ -778,8 +803,8 @@ public abstract class RouterFunctions {
|
|||
}
|
||||
}
|
||||
|
||||
private static final class DefaultRouterFunction<T extends ServerResponse>
|
||||
extends AbstractRouterFunction<T> {
|
||||
|
||||
private static final class DefaultRouterFunction<T extends ServerResponse> extends AbstractRouterFunction<T> {
|
||||
|
||||
private final RequestPredicate predicate;
|
||||
|
||||
|
@ -812,8 +837,8 @@ public abstract class RouterFunctions {
|
|||
|
||||
}
|
||||
|
||||
private static final class DefaultNestedRouterFunction<T extends ServerResponse>
|
||||
extends AbstractRouterFunction<T> {
|
||||
|
||||
private static final class DefaultNestedRouterFunction<T extends ServerResponse> extends AbstractRouterFunction<T> {
|
||||
|
||||
private final RequestPredicate predicate;
|
||||
|
||||
|
@ -858,6 +883,7 @@ public abstract class RouterFunctions {
|
|||
|
||||
}
|
||||
|
||||
|
||||
private static class ResourcesRouterFunction extends AbstractRouterFunction<ServerResponse> {
|
||||
|
||||
private final Function<ServerRequest, Optional<Resource>> lookupFunction;
|
||||
|
|
|
@ -77,9 +77,6 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
|
|||
* {@link RouterFunction} instances available in the application context.
|
||||
*/
|
||||
public RouterFunctionMapping() {
|
||||
// gh-23236 will ensure the configured parser is used to parse patterns
|
||||
// For now this helps to signal to the DispatcherServlet the need to initialize the RequestPath
|
||||
setPatternParser(new PathPatternParser());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -135,6 +132,14 @@ public class RouterFunctionMapping extends AbstractHandlerMapping implements Ini
|
|||
if (CollectionUtils.isEmpty(this.messageConverters)) {
|
||||
initMessageConverters();
|
||||
}
|
||||
if (this.routerFunction != null) {
|
||||
PathPatternParser patternParser = getPatternParser();
|
||||
if (patternParser == null) {
|
||||
patternParser = new PathPatternParser();
|
||||
setPatternParser(patternParser);
|
||||
}
|
||||
RouterFunctions.changeParser(this.routerFunction, patternParser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright 2002-2020 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.servlet.function.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.web.servlet.HandlerExecutionChain;
|
||||
import org.springframework.web.servlet.function.HandlerFunction;
|
||||
import org.springframework.web.servlet.function.RouterFunction;
|
||||
import org.springframework.web.servlet.function.RouterFunctions;
|
||||
import org.springframework.web.servlet.function.ServerResponse;
|
||||
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
|
||||
import org.springframework.web.util.ServletRequestPathUtils;
|
||||
import org.springframework.web.util.pattern.PathPatternParser;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
class RouterFunctionMappingTests {
|
||||
|
||||
private final MockHttpServletRequest request = new MockHttpServletRequest("GET", "/match");
|
||||
|
||||
private List<HttpMessageConverter<?>> messageConverters = Collections.emptyList();
|
||||
|
||||
@Test
|
||||
public void normal() throws Exception {
|
||||
HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.ok().build();
|
||||
RouterFunction<ServerResponse> routerFunction = request -> Optional.of(handlerFunction);
|
||||
|
||||
RouterFunctionMapping mapping = new RouterFunctionMapping(routerFunction);
|
||||
mapping.setMessageConverters(this.messageConverters);
|
||||
ServletRequestPathUtils.parseAndCache(this.request);
|
||||
|
||||
HandlerExecutionChain result = mapping.getHandler(this.request);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getHandler()).isSameAs(handlerFunction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noMatch() throws Exception {
|
||||
RouterFunction<ServerResponse> routerFunction = request -> Optional.empty();
|
||||
|
||||
RouterFunctionMapping mapping = new RouterFunctionMapping(routerFunction);
|
||||
mapping.setMessageConverters(this.messageConverters);
|
||||
ServletRequestPathUtils.parseAndCache(this.request);
|
||||
|
||||
HandlerExecutionChain result = mapping.getHandler(this.request);
|
||||
|
||||
assertThat(result).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeParser() throws Exception {
|
||||
HandlerFunction<ServerResponse> handlerFunction = request -> ServerResponse.ok().build();
|
||||
RouterFunction<ServerResponse> routerFunction = RouterFunctions.route()
|
||||
.GET("/foo", handlerFunction)
|
||||
.POST("/bar", handlerFunction)
|
||||
.build();
|
||||
|
||||
RouterFunctionMapping mapping = new RouterFunctionMapping(routerFunction);
|
||||
mapping.setMessageConverters(this.messageConverters);
|
||||
PathPatternParser patternParser = new PathPatternParser();
|
||||
patternParser.setCaseSensitive(false);
|
||||
mapping.setPatternParser(patternParser);
|
||||
mapping.afterPropertiesSet();
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/FOO");
|
||||
ServletRequestPathUtils.parseAndCache(request);
|
||||
|
||||
HandlerExecutionChain result = mapping.getHandler(request);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getHandler()).isSameAs(handlerFunction);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue