Refactor HandlerMapping path match configuration
Since the introduction of `PathPatternRegistry`, the various path match configuration flags are no longer needed in several places and that configuration can live in the registry itself. Issue: SPR-14544
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.web.reactive.config;
|
||||
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.server.support.HttpRequestPathHelper;
|
||||
|
||||
/**
|
||||
@@ -27,24 +26,22 @@ import org.springframework.web.server.support.HttpRequestPathHelper;
|
||||
*/
|
||||
public class PathMatchConfigurer {
|
||||
|
||||
private Boolean suffixPatternMatch;
|
||||
private boolean suffixPatternMatch = false;
|
||||
|
||||
private Boolean trailingSlashMatch;
|
||||
private boolean trailingSlashMatch = true;
|
||||
|
||||
private Boolean registeredSuffixPatternMatch;
|
||||
private boolean registeredSuffixPatternMatch = false;
|
||||
|
||||
private HttpRequestPathHelper pathHelper;
|
||||
|
||||
private PathMatcher pathMatcher;
|
||||
|
||||
|
||||
/**
|
||||
* Whether to use suffix pattern match (".*") when matching patterns to
|
||||
* requests. If enabled a method mapped to "/users" also matches to "/users.*".
|
||||
* <p>By default this is set to {@code true}.
|
||||
* <p>By default this is set to {@code false}.
|
||||
* @see #registeredSuffixPatternMatch
|
||||
*/
|
||||
public PathMatchConfigurer setUseSuffixPatternMatch(Boolean suffixPatternMatch) {
|
||||
public PathMatchConfigurer setUseSuffixPatternMatch(boolean suffixPatternMatch) {
|
||||
this.suffixPatternMatch = suffixPatternMatch;
|
||||
return this;
|
||||
}
|
||||
@@ -54,7 +51,7 @@ public class PathMatchConfigurer {
|
||||
* If enabled a method mapped to "/users" also matches to "/users/".
|
||||
* <p>The default value is {@code true}.
|
||||
*/
|
||||
public PathMatchConfigurer setUseTrailingSlashMatch(Boolean trailingSlashMatch) {
|
||||
public PathMatchConfigurer setUseTrailingSlashMatch(boolean trailingSlashMatch) {
|
||||
this.trailingSlashMatch = trailingSlashMatch;
|
||||
return this;
|
||||
}
|
||||
@@ -64,9 +61,9 @@ public class PathMatchConfigurer {
|
||||
* that are explicitly registered. This is generally recommended to reduce
|
||||
* ambiguity and to avoid issues such as when a "." (dot) appears in the path
|
||||
* for other reasons.
|
||||
* <p>By default this is set to "true".
|
||||
* <p>By default this is set to "false".
|
||||
*/
|
||||
public PathMatchConfigurer setUseRegisteredSuffixPatternMatch(Boolean registeredSuffixPatternMatch) {
|
||||
public PathMatchConfigurer setUseRegisteredSuffixPatternMatch(boolean registeredSuffixPatternMatch) {
|
||||
this.registeredSuffixPatternMatch = registeredSuffixPatternMatch;
|
||||
return this;
|
||||
}
|
||||
@@ -80,24 +77,15 @@ public class PathMatchConfigurer {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PathMatcher for matching URL paths against registered URL patterns.
|
||||
* <p>Default is {@link org.springframework.util.AntPathMatcher AntPathMatcher}.
|
||||
*/
|
||||
public PathMatchConfigurer setPathMatcher(PathMatcher pathMatcher) {
|
||||
this.pathMatcher = pathMatcher;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected Boolean isUseSuffixPatternMatch() {
|
||||
protected boolean isUseSuffixPatternMatch() {
|
||||
return this.suffixPatternMatch;
|
||||
}
|
||||
|
||||
protected Boolean isUseTrailingSlashMatch() {
|
||||
protected boolean isUseTrailingSlashMatch() {
|
||||
return this.trailingSlashMatch;
|
||||
}
|
||||
|
||||
protected Boolean isUseRegisteredSuffixPatternMatch() {
|
||||
protected boolean isUseRegisteredSuffixPatternMatch() {
|
||||
return this.registeredSuffixPatternMatch;
|
||||
}
|
||||
|
||||
@@ -105,9 +93,4 @@ public class PathMatchConfigurer {
|
||||
return this.pathHelper;
|
||||
}
|
||||
|
||||
//TODO: remove
|
||||
protected PathMatcher getPathMatcher() {
|
||||
return this.pathMatcher;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ import org.springframework.web.reactive.result.view.ViewResolver;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebExceptionHandler;
|
||||
import org.springframework.web.server.handler.ResponseStatusExceptionHandler;
|
||||
import org.springframework.web.util.patterns.PathPatternRegistry;
|
||||
|
||||
/**
|
||||
* The main class for Spring Web Reactive configuration.
|
||||
@@ -136,25 +137,23 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
||||
|
||||
@Bean
|
||||
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
|
||||
CompositeContentTypeResolver contentTypeResolver = webFluxContentTypeResolver();
|
||||
RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
|
||||
mapping.setOrder(0);
|
||||
mapping.setContentTypeResolver(webFluxContentTypeResolver());
|
||||
mapping.setContentTypeResolver(contentTypeResolver);
|
||||
mapping.setCorsConfigurations(getCorsConfigurations());
|
||||
PathPatternRegistry pathPatternRegistry = new PathPatternRegistry();
|
||||
mapping.setPatternRegistry(pathPatternRegistry);
|
||||
|
||||
PathMatchConfigurer configurer = getPathMatchConfigurer();
|
||||
if (configurer.isUseSuffixPatternMatch() != null) {
|
||||
mapping.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
|
||||
}
|
||||
if (configurer.isUseRegisteredSuffixPatternMatch() != null) {
|
||||
mapping.setUseRegisteredSuffixPatternMatch(configurer.isUseRegisteredSuffixPatternMatch());
|
||||
}
|
||||
if (configurer.isUseTrailingSlashMatch() != null) {
|
||||
mapping.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
|
||||
pathPatternRegistry.setUseSuffixPatternMatch(configurer.isUseSuffixPatternMatch());
|
||||
pathPatternRegistry.setUseTrailingSlashMatch(configurer.isUseTrailingSlashMatch());
|
||||
if (configurer.isUseRegisteredSuffixPatternMatch() && contentTypeResolver != null) {
|
||||
pathPatternRegistry.setFileExtensions(contentTypeResolver.getKeys());
|
||||
}
|
||||
if (configurer.getPathHelper() != null) {
|
||||
mapping.setPathHelper(configurer.getPathHelper());
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,15 +18,11 @@ package org.springframework.web.reactive.result.condition;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.support.HttpRequestPathHelper;
|
||||
import org.springframework.web.util.patterns.PathPattern;
|
||||
@@ -37,6 +33,7 @@ import org.springframework.web.util.patterns.PathPatternRegistry;
|
||||
* against a set of URL path patterns.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @since 5.0
|
||||
*/
|
||||
public final class PatternsRequestCondition extends AbstractRequestCondition<PatternsRequestCondition> {
|
||||
@@ -51,7 +48,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
* @param patterns 0 or more URL patterns; if 0 the condition will match to every request.
|
||||
*/
|
||||
public PatternsRequestCondition(String... patterns) {
|
||||
this(patterns, null, false, false, null);
|
||||
this(patterns, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,45 +56,22 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
|
||||
* Each pattern that is not empty and does not start with "/" is pre-pended with "/".
|
||||
* @param patterns the URL patterns to use; if 0, the condition will match to every request.
|
||||
* @param pathHelper to determine the lookup path for a request
|
||||
* @param useSuffixPatternMatch whether to enable matching by suffix (".*")
|
||||
* @param useTrailingSlashMatch whether to match irrespective of a trailing slash
|
||||
* @param extensions file extensions to consider for path matching
|
||||
* @param pathPatternRegistry the pattern registry in which we'll register the given paths
|
||||
*/
|
||||
public PatternsRequestCondition(String[] patterns, HttpRequestPathHelper pathHelper,
|
||||
boolean useSuffixPatternMatch, boolean useTrailingSlashMatch, Set<String> extensions) {
|
||||
|
||||
this(createPatternSet(patterns, useSuffixPatternMatch, useTrailingSlashMatch, extensions),
|
||||
PathPatternRegistry pathPatternRegistry) {
|
||||
this(createPatternSet(patterns, pathPatternRegistry),
|
||||
(pathHelper != null ? pathHelper : new HttpRequestPathHelper()));
|
||||
}
|
||||
|
||||
private static PathPatternRegistry createPatternSet(String[] patterns,boolean useSuffixPatternMatch,
|
||||
boolean useTrailingSlashMatch, Set<String> extensions) {
|
||||
Set<String> fixedFileExtensions = (extensions != null) ? extensions.stream()
|
||||
.map(ext -> (ext.charAt(0) != '.') ? "." + ext : ext)
|
||||
.collect(Collectors.toSet()) : Collections.emptySet();
|
||||
PathPatternRegistry patternSet = new PathPatternRegistry();
|
||||
patternSet.setUseSuffixPatternMatch(useSuffixPatternMatch);
|
||||
patternSet.setUseTrailingSlashMatch(useTrailingSlashMatch);
|
||||
patternSet.setFileExtensions(extensions);
|
||||
private static PathPatternRegistry createPatternSet(String[] patterns, PathPatternRegistry pathPatternRegistry) {
|
||||
PathPatternRegistry patternSet = pathPatternRegistry != null ? pathPatternRegistry : new PathPatternRegistry();
|
||||
if(patterns != null) {
|
||||
Arrays.asList(patterns).stream()
|
||||
.map(prependLeadingSlash())
|
||||
.forEach(p -> patternSet.register(p));
|
||||
Arrays.asList(patterns).stream().forEach(p -> patternSet.register(p));
|
||||
}
|
||||
return patternSet;
|
||||
}
|
||||
|
||||
private static Function<String, String> prependLeadingSlash() {
|
||||
return pattern -> {
|
||||
if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
|
||||
return "/" + pattern;
|
||||
}
|
||||
else {
|
||||
return pattern;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private PatternsRequestCondition(PathPatternRegistry patternRegistry, HttpRequestPathHelper pathHelper) {
|
||||
this.patternRegistry = patternRegistry;
|
||||
this.pathHelper = pathHelper;
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.springframework.web.reactive.result.condition.RequestConditionHolder;
|
||||
import org.springframework.web.reactive.result.condition.RequestMethodsRequestCondition;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.support.HttpRequestPathHelper;
|
||||
import org.springframework.web.util.patterns.PathPatternRegistry;
|
||||
|
||||
/**
|
||||
* Encapsulates the following request mapping conditions:
|
||||
@@ -470,10 +471,15 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
public RequestMappingInfo build() {
|
||||
RequestedContentTypeResolver contentTypeResolver = this.options.getContentTypeResolver();
|
||||
|
||||
PathPatternRegistry pathPatternRegistry = this.options.getPathPatternRegistry();
|
||||
PathPatternRegistry conditionRegistry = new PathPatternRegistry();
|
||||
conditionRegistry.setUseTrailingSlashMatch(pathPatternRegistry.useTrailingSlashMatch());
|
||||
conditionRegistry.setUseSuffixPatternMatch(pathPatternRegistry.useSuffixPatternMatch());
|
||||
conditionRegistry.setFileExtensions(pathPatternRegistry.getFileExtensions());
|
||||
|
||||
|
||||
PatternsRequestCondition patternsCondition = new PatternsRequestCondition(
|
||||
this.paths, this.options.getPathHelper(),
|
||||
this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
|
||||
this.options.getFileExtensions());
|
||||
this.paths, this.options.getPathHelper(), conditionRegistry);
|
||||
|
||||
return new RequestMappingInfo(this.mappingName, patternsCondition,
|
||||
new RequestMethodsRequestCondition(methods),
|
||||
@@ -496,9 +502,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
|
||||
private HttpRequestPathHelper pathHelper;
|
||||
|
||||
private boolean trailingSlashMatch = true;
|
||||
|
||||
private boolean suffixPatternMatch = false;
|
||||
private PathPatternRegistry pathPatternRegistry;
|
||||
|
||||
private boolean registeredSuffixPatternMatch = false;
|
||||
|
||||
@@ -516,29 +520,22 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
return this.pathHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to apply trailing slash matching in PatternsRequestCondition.
|
||||
* <p>By default this is set to 'true'.
|
||||
*/
|
||||
public void setTrailingSlashMatch(boolean trailingSlashMatch) {
|
||||
this.trailingSlashMatch = trailingSlashMatch;
|
||||
}
|
||||
public PathPatternRegistry getPathPatternRegistry() {
|
||||
if(this.pathPatternRegistry == null) {
|
||||
this.pathPatternRegistry = new PathPatternRegistry();
|
||||
this.pathPatternRegistry.setUseTrailingSlashMatch(true);
|
||||
}
|
||||
if(this.registeredSuffixPatternMatch) {
|
||||
RequestedContentTypeResolver resolver = getContentTypeResolver();
|
||||
if (resolver != null && resolver instanceof MappingContentTypeResolver) {
|
||||
if (resolver instanceof MappingContentTypeResolver) {
|
||||
Set<String> fileExtensions = ((MappingContentTypeResolver) resolver).getKeys();
|
||||
this.pathPatternRegistry.setFileExtensions(fileExtensions);
|
||||
}
|
||||
|
||||
public boolean useTrailingSlashMatch() {
|
||||
return this.trailingSlashMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to apply suffix pattern matching in PatternsRequestCondition.
|
||||
* <p>By default this is set to 'true'.
|
||||
* @see #setRegisteredSuffixPatternMatch(boolean)
|
||||
*/
|
||||
public void setSuffixPatternMatch(boolean suffixPatternMatch) {
|
||||
this.suffixPatternMatch = suffixPatternMatch;
|
||||
}
|
||||
|
||||
public boolean useSuffixPatternMatch() {
|
||||
return this.suffixPatternMatch;
|
||||
}
|
||||
}
|
||||
return this.pathPatternRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -550,7 +547,6 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
*/
|
||||
public void setRegisteredSuffixPatternMatch(boolean registeredSuffixPatternMatch) {
|
||||
this.registeredSuffixPatternMatch = registeredSuffixPatternMatch;
|
||||
this.suffixPatternMatch = (registeredSuffixPatternMatch || this.suffixPatternMatch);
|
||||
}
|
||||
|
||||
public boolean useRegisteredSuffixPatternMatch() {
|
||||
@@ -558,18 +554,12 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file extensions to use for suffix pattern matching. If
|
||||
* {@code registeredSuffixPatternMatch=true}, the extensions are obtained
|
||||
* from the configured {@code contentTypeResolver}.
|
||||
* Set the PathPatternRegistry to use for parsing and matching path patterns
|
||||
* <p>By default, a new instance of {@link PathPatternRegistry} with
|
||||
* {@link PathPatternRegistry#setUseTrailingSlashMatch(boolean)} set to {@code true}
|
||||
*/
|
||||
public Set<String> getFileExtensions() {
|
||||
RequestedContentTypeResolver resolver = getContentTypeResolver();
|
||||
if (useRegisteredSuffixPatternMatch() && resolver != null) {
|
||||
if (resolver instanceof MappingContentTypeResolver) {
|
||||
return ((MappingContentTypeResolver) resolver).getKeys();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
public void setPathPatternRegistry(PathPatternRegistry pathPatternRegistry) {
|
||||
this.pathPatternRegistry = pathPatternRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.EmbeddedValueResolverAware;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
@@ -48,53 +47,12 @@ import org.springframework.web.reactive.result.method.RequestMappingInfoHandlerM
|
||||
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
|
||||
implements EmbeddedValueResolverAware {
|
||||
|
||||
private boolean useSuffixPatternMatch = false;
|
||||
|
||||
private boolean useRegisteredSuffixPatternMatch = false;
|
||||
|
||||
private boolean useTrailingSlashMatch = true;
|
||||
|
||||
private RequestedContentTypeResolver contentTypeResolver = new RequestedContentTypeResolverBuilder().build();
|
||||
|
||||
private StringValueResolver embeddedValueResolver;
|
||||
|
||||
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
|
||||
|
||||
|
||||
/**
|
||||
* Whether to use suffix pattern matching. If enabled a method mapped to
|
||||
* "/path" also matches to "/path.*".
|
||||
* <p>The default value is {@code true}.
|
||||
* <p><strong>Note:</strong> when using suffix pattern matching it's usually
|
||||
* preferable to be explicit about what is and isn't an extension so rather
|
||||
* than setting this property consider using
|
||||
* {@link #setUseRegisteredSuffixPatternMatch} instead.
|
||||
*/
|
||||
public void setUseSuffixPatternMatch(boolean useSuffixPatternMatch) {
|
||||
this.useSuffixPatternMatch = useSuffixPatternMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether suffix pattern matching should work only against path extensions
|
||||
* explicitly registered with the configured {@link RequestedContentTypeResolver}. This
|
||||
* is generally recommended to reduce ambiguity and to avoid issues such as
|
||||
* when a "." appears in the path for other reasons.
|
||||
* <p>By default this is set to "true".
|
||||
*/
|
||||
public void setUseRegisteredSuffixPatternMatch(boolean useRegisteredSuffixPatternMatch) {
|
||||
this.useRegisteredSuffixPatternMatch = useRegisteredSuffixPatternMatch;
|
||||
this.useSuffixPatternMatch = (useRegisteredSuffixPatternMatch || this.useSuffixPatternMatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to match to URLs irrespective of the presence of a trailing slash.
|
||||
* If enabled a method mapped to "/users" also matches to "/users/".
|
||||
* <p>The default value is {@code true}.
|
||||
*/
|
||||
public void setUseTrailingSlashMatch(boolean useTrailingSlashMatch) {
|
||||
this.useTrailingSlashMatch = useTrailingSlashMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link RequestedContentTypeResolver} to use to determine requested media types.
|
||||
* If not set, the default constructor is used.
|
||||
@@ -113,36 +71,11 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
public void afterPropertiesSet() {
|
||||
this.config = new RequestMappingInfo.BuilderConfiguration();
|
||||
this.config.setPathHelper(getPathHelper());
|
||||
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
|
||||
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
|
||||
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
|
||||
this.config.setContentTypeResolver(getContentTypeResolver());
|
||||
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Whether to use suffix pattern matching.
|
||||
*/
|
||||
public boolean useSuffixPatternMatch() {
|
||||
return this.useSuffixPatternMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use registered suffixes for pattern matching.
|
||||
*/
|
||||
public boolean useRegisteredSuffixPatternMatch() {
|
||||
return this.useRegisteredSuffixPatternMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to match to URLs irrespective of the presence of a trailing slash.
|
||||
*/
|
||||
public boolean useTrailingSlashMatch() {
|
||||
return this.useTrailingSlashMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured {@link RequestedContentTypeResolver}.
|
||||
*/
|
||||
@@ -150,14 +83,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
|
||||
return this.contentTypeResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file extensions to use for suffix pattern matching.
|
||||
*/
|
||||
public Set<String> getFileExtensions() {
|
||||
return this.config.getFileExtensions();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* Expects a handler to have a type-level @{@link Controller} annotation.
|
||||
|
||||
Reference in New Issue
Block a user