Remove Spring MVC suffixPattern and trailingSlash matching

See gh-34036
This commit is contained in:
rstoyanchev
2024-12-16 17:52:49 +00:00
parent ca909483f1
commit e9946937e7
20 changed files with 31 additions and 960 deletions

View File

@@ -370,16 +370,6 @@ public interface MockMvcWebTestClient {
*/
ControllerSpec patternParser(PathPatternParser parser);
/**
* Whether to match trailing slashes.
* <p>This is delegated to
* {@link StandaloneMockMvcBuilder#setUseTrailingSlashPatternMatch(boolean)}.
* @deprecated as of 6.0, see
* {@link PathPatternParser#setMatchOptionalTrailingSeparator(boolean)}
*/
@Deprecated(since = "6.0")
ControllerSpec useTrailingSlashPatternMatch(boolean useTrailingSlashPatternMatch);
/**
* Configure placeholder values to use.
* <p>This is delegated to

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
@@ -152,13 +152,6 @@ class StandaloneMockMvcSpec extends AbstractMockMvcServerSpec<MockMvcWebTestClie
return this;
}
@SuppressWarnings("deprecation")
@Override
public StandaloneMockMvcSpec useTrailingSlashPatternMatch(boolean useTrailingSlashPatternMatch) {
this.mockMvcBuilder.setUseTrailingSlashPatternMatch(useTrailingSlashPatternMatch);
return this;
}
@Override
public StandaloneMockMvcSpec placeholderValue(String name, String value) {
this.mockMvcBuilder.addPlaceholderValue(name, value);

View File

@@ -130,10 +130,6 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder<StandaloneM
@Nullable
private PathPatternParser patternParser;
private boolean useSuffixPatternMatch = false;
private boolean useTrailingSlashPatternMatch = false;
@Nullable
private Boolean removeSemicolonContent;
@@ -323,33 +319,6 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder<StandaloneM
return this;
}
/**
* Whether to use suffix pattern match (".*") when matching patterns to
* requests. If enabled a method mapped to "/users" also matches to "/users.*".
* <p>The default value is {@code false}.
* @deprecated as of 5.2.4. See class-level note in
* {@link RequestMappingHandlerMapping} on the deprecation of path extension
* config options.
*/
@Deprecated
public StandaloneMockMvcBuilder setUseSuffixPatternMatch(boolean useSuffixPatternMatch) {
this.useSuffixPatternMatch = useSuffixPatternMatch;
this.preferPathMatcher |= useSuffixPatternMatch;
return this;
}
/**
* Whether to match to URLs irrespective of the presence of a trailing slash.
* If enabled a method mapped to "/users" also matches to "/users/".
* @deprecated as of 6.0, see
* {@link PathPatternParser#setMatchOptionalTrailingSeparator(boolean)}
*/
@Deprecated(since = "6.0")
public StandaloneMockMvcBuilder setUseTrailingSlashPatternMatch(boolean useTrailingSlashPatternMatch) {
this.useTrailingSlashPatternMatch = useTrailingSlashPatternMatch;
return this;
}
/**
* Set if ";" (semicolon) content should be stripped from the request URI. The value,
* if provided, is in turn set on
@@ -469,7 +438,6 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder<StandaloneM
/** Using the MVC Java configuration as the starting point for the "standalone" setup. */
private class StandaloneConfiguration extends WebMvcConfigurationSupport {
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping getHandlerMapping(
FormattingConversionService mvcConversionService,
ResourceUrlProvider mvcResourceUrlProvider) {
@@ -478,7 +446,6 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder<StandaloneM
handlerMapping.setEmbeddedValueResolver(new StaticStringValueResolver(placeholderValues));
if (patternParser == null && preferPathMatcher) {
handlerMapping.setPatternParser(null);
handlerMapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
if (removeSemicolonContent != null) {
UrlPathHelper pathHelper = new UrlPathHelper();
pathHelper.setRemoveSemicolonContent(removeSemicolonContent);
@@ -488,7 +455,6 @@ public class StandaloneMockMvcBuilder extends AbstractMockMvcBuilder<StandaloneM
else if (patternParser != null) {
handlerMapping.setPatternParser(patternParser);
}
handlerMapping.setUseTrailingSlashMatch(useTrailingSlashPatternMatch);
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));
return handlerMapping;

View File

@@ -74,25 +74,6 @@ class StandaloneMockMvcBuilderTests {
assertThat(((HandlerMethod) chain.getHandler()).getMethod().getName()).isEqualTo("handleWithPlaceholders");
}
@Test // SPR-13637
@SuppressWarnings("deprecation")
void suffixPatternMatch() throws Exception {
TestStandaloneMockMvcBuilder builder = new TestStandaloneMockMvcBuilder(new PersonController());
builder.setUseSuffixPatternMatch(false);
builder.build();
RequestMappingHandlerMapping hm = builder.wac.getBean(RequestMappingHandlerMapping.class);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/persons");
HandlerExecutionChain chain = hm.getHandler(request);
assertThat(chain).isNotNull();
assertThat(((HandlerMethod) chain.getHandler()).getMethod().getName()).isEqualTo("persons");
request = new MockHttpServletRequest("GET", "/persons.xml");
chain = hm.getHandler(request);
assertThat(chain).isNull();
}
@Test // SPR-12553
void applicationContextAttribute() {
TestStandaloneMockMvcBuilder builder = new TestStandaloneMockMvcBuilder(new PlaceholderController());

View File

@@ -409,21 +409,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
Element pathMatchingElement = DomUtils.getChildElementByTagName(element, "path-matching");
Object source = context.extractSource(element);
if (pathMatchingElement != null) {
if (pathMatchingElement.hasAttribute("trailing-slash")) {
boolean useTrailingSlashMatch = Boolean.parseBoolean(pathMatchingElement.getAttribute("trailing-slash"));
handlerMappingDef.getPropertyValues().add("useTrailingSlashMatch", useTrailingSlashMatch);
}
boolean preferPathMatcher = false;
if (pathMatchingElement.hasAttribute("suffix-pattern")) {
boolean useSuffixPatternMatch = Boolean.parseBoolean(pathMatchingElement.getAttribute("suffix-pattern"));
handlerMappingDef.getPropertyValues().add("useSuffixPatternMatch", useSuffixPatternMatch);
preferPathMatcher |= useSuffixPatternMatch;
}
if (pathMatchingElement.hasAttribute("registered-suffixes-only")) {
boolean useRegisteredSuffixPatternMatch = Boolean.parseBoolean(pathMatchingElement.getAttribute("registered-suffixes-only"));
handlerMappingDef.getPropertyValues().add("useRegisteredSuffixPatternMatch", useRegisteredSuffixPatternMatch);
preferPathMatcher |= useRegisteredSuffixPatternMatch;
}
RuntimeBeanReference pathHelperRef = null;
if (pathMatchingElement.hasAttribute("path-helper")) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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,7 +23,6 @@ import java.util.function.Predicate;
import org.springframework.lang.Nullable;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;
@@ -49,18 +48,9 @@ public class PathMatchConfigurer {
@Nullable
private PathPatternParser patternParser;
@Nullable
private Boolean trailingSlashMatch;
@Nullable
private Map<String, Predicate<Class<?>>> pathPrefixes;
@Nullable
private Boolean suffixPatternMatch;
@Nullable
private Boolean registeredSuffixPatternMatch;
@Nullable
private UrlPathHelper urlPathHelper;
@@ -84,8 +74,6 @@ public class PathMatchConfigurer {
* <p><strong>Note:</strong> This property is mutually exclusive with the
* following other, {@code AntPathMatcher} related properties:
* <ul>
* <li>{@link #setUseSuffixPatternMatch(Boolean)}
* <li>{@link #setUseRegisteredSuffixPatternMatch(Boolean)}
* <li>{@link #setUrlPathHelper(UrlPathHelper)}
* <li>{@link #setPathMatcher(PathMatcher)}
* </ul>
@@ -103,20 +91,6 @@ public class PathMatchConfigurer {
return this;
}
/**
* 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 was changed in 6.0 from {@code true} to {@code false} in
* order to support the deprecation of the property.
* @deprecated as of 6.0, see
* {@link PathPatternParser#setMatchOptionalTrailingSeparator(boolean)}
*/
@Deprecated(since = "6.0")
public PathMatchConfigurer setUseTrailingSlashMatch(Boolean trailingSlashMatch) {
this.trailingSlashMatch = trailingSlashMatch;
return this;
}
/**
* Configure a path prefix to apply to matching controller methods.
* <p>Prefixes are used to enrich the mappings of every {@code @RequestMapping}
@@ -136,49 +110,6 @@ public class PathMatchConfigurer {
return this;
}
/**
* Whether to use suffix pattern match (".*") when matching patterns to
* requests. If enabled a method mapped to "/users" also matches to "/users.*".
* <p><strong>Note:</strong> This property is mutually exclusive with
* {@link #setPatternParser(PathPatternParser)}. If set, it enables use of
* String path matching, unless a {@code PathPatternParser} is also
* explicitly set in which case this property is ignored.
* <p>By default this is set to {@code false}.
* @deprecated as of 5.2.4. See class-level note in
* {@link RequestMappingHandlerMapping} on the deprecation of path extension
* config options. As there is no replacement for this method, in 5.2.x it is
* necessary to set it to {@code false}. In 5.3 the default changes to
* {@code false} and use of this property becomes unnecessary.
*/
@Deprecated
public PathMatchConfigurer setUseSuffixPatternMatch(@Nullable Boolean suffixPatternMatch) {
this.suffixPatternMatch = suffixPatternMatch;
this.preferPathMatcher |= (suffixPatternMatch != null && suffixPatternMatch);
return this;
}
/**
* Whether suffix pattern matching should work only against path extensions
* explicitly registered when you
* {@link WebMvcConfigurer#configureContentNegotiation configure content
* negotiation}. This is generally recommended to reduce ambiguity and to
* avoid issues such as when a "." appears in the path for other reasons.
* <p><strong>Note:</strong> This property is mutually exclusive with
* {@link #setPatternParser(PathPatternParser)}. If set, it enables use of
* String path matching, unless a {@code PathPatternParser} is also
* explicitly set in which case this property is ignored.
* <p>By default this is set to "false".
* @deprecated as of 5.2.4. See class-level note in
* {@link RequestMappingHandlerMapping} on the deprecation of path extension
* config options.
*/
@Deprecated
public PathMatchConfigurer setUseRegisteredSuffixPatternMatch(@Nullable Boolean registeredSuffixPatternMatch) {
this.registeredSuffixPatternMatch = registeredSuffixPatternMatch;
this.preferPathMatcher |= (registeredSuffixPatternMatch != null && registeredSuffixPatternMatch);
return this;
}
/**
* Set the UrlPathHelper to use to resolve the mapping path for the application.
* <p><strong>Note:</strong> This property is mutually exclusive with
@@ -232,39 +163,11 @@ public class PathMatchConfigurer {
return this.patternParser;
}
@Nullable
@Deprecated
public Boolean isUseTrailingSlashMatch() {
return this.trailingSlashMatch;
}
@Nullable
protected Map<String, Predicate<Class<?>>> getPathPrefixes() {
return this.pathPrefixes;
}
/**
* Whether to use registered suffixes for pattern matching.
* @deprecated as of 5.2.4, see deprecation note on
* {@link #setUseRegisteredSuffixPatternMatch(Boolean)}.
*/
@Nullable
@Deprecated
public Boolean isUseRegisteredSuffixPatternMatch() {
return this.registeredSuffixPatternMatch;
}
/**
* Whether to use registered suffixes for pattern matching.
* @deprecated as of 5.2.4, see deprecation note on
* {@link #setUseSuffixPatternMatch(Boolean)}.
*/
@Nullable
@Deprecated
public Boolean isUseSuffixPatternMatch() {
return this.suffixPatternMatch;
}
@Nullable
public UrlPathHelper getUrlPathHelper() {
return this.urlPathHelper;

View File

@@ -321,22 +321,6 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
initHandlerMapping(mapping, conversionService, resourceUrlProvider);
PathMatchConfigurer pathConfig = getPathMatchConfigurer();
if (pathConfig.preferPathMatcher()) {
Boolean useSuffixPatternMatch = pathConfig.isUseSuffixPatternMatch();
if (useSuffixPatternMatch != null) {
mapping.setUseSuffixPatternMatch(useSuffixPatternMatch);
}
Boolean useRegisteredSuffixPatternMatch = pathConfig.isUseRegisteredSuffixPatternMatch();
if (useRegisteredSuffixPatternMatch != null) {
mapping.setUseRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch);
}
}
Boolean useTrailingSlashMatch = pathConfig.isUseTrailingSlashMatch();
if (useTrailingSlashMatch != null) {
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
}
if (pathConfig.getPathPrefixes() != null) {
mapping.setPathPrefixes(pathConfig.getPathPrefixes());
}
@@ -520,6 +504,8 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
else if (pathConfig.getPatternParser() != null) {
mapping.setPatternParser(pathConfig.getPatternParser());
}
// else: AbstractHandlerMapping defaults to PathPatternParser
mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));
mapping.setCorsConfigurations(getCorsConfigurations());
}

View File

@@ -60,12 +60,6 @@ public class PatternsRequestCondition extends AbstractRequestCondition<PatternsR
private final PathMatcher pathMatcher;
private final boolean useSuffixPatternMatch;
private final boolean useTrailingSlashMatch;
private final List<String> fileExtensions = new ArrayList<>();
/**
* Constructor with URL patterns which are prepended with "/" if necessary.
@@ -73,85 +67,20 @@ public class PatternsRequestCondition extends AbstractRequestCondition<PatternsR
* path {@code ""} mapping which matches all requests.
*/
public PatternsRequestCondition(String... patterns) {
this(patterns, true, null);
this(patterns, null, null);
}
/**
* Variant of {@link #PatternsRequestCondition(String...)} with a
* {@link PathMatcher} and flag for matching trailing slashes.
* @since 5.3
* Constructor with URL patterns which are prepended with "/" if necessary.
* @param patterns 0 or more URL patterns; no patterns results in an empty
* path {@code ""} mapping which matches all requests.
* @since 7.0
*/
public PatternsRequestCondition(String[] patterns, boolean useTrailingSlashMatch,
@Nullable PathMatcher pathMatcher) {
this(patterns, null, pathMatcher, useTrailingSlashMatch);
}
/**
* Variant of {@link #PatternsRequestCondition(String...)} with a
* {@link UrlPathHelper} and a {@link PathMatcher}, and whether to match
* trailing slashes.
* <p>As of 5.3 the path is obtained through the static method
* {@link UrlPathHelper#getResolvedLookupPath} and a {@code UrlPathHelper}
* does not need to be passed in.
* @since 5.2.4
* @deprecated as of 5.3 in favor of
* {@link #PatternsRequestCondition(String[], boolean, PathMatcher)}.
*/
@Deprecated
public PatternsRequestCondition(String[] patterns, @Nullable UrlPathHelper urlPathHelper,
@Nullable PathMatcher pathMatcher, boolean useTrailingSlashMatch) {
this(patterns, urlPathHelper, pathMatcher, false, useTrailingSlashMatch);
}
/**
* Variant of {@link #PatternsRequestCondition(String...)} with a
* {@link UrlPathHelper} and a {@link PathMatcher}, and flags for matching
* with suffixes and trailing slashes.
* <p>As of 5.3 the path is obtained through the static method
* {@link UrlPathHelper#getResolvedLookupPath} and a {@code UrlPathHelper}
* does not need to be passed in.
* @deprecated as of 5.2.4. See class-level note in
* {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping}
* on the deprecation of path extension config options.
*/
@Deprecated
public PatternsRequestCondition(String[] patterns, @Nullable UrlPathHelper urlPathHelper,
@Nullable PathMatcher pathMatcher, boolean useSuffixPatternMatch, boolean useTrailingSlashMatch) {
this(patterns, urlPathHelper, pathMatcher, useSuffixPatternMatch, useTrailingSlashMatch, null);
}
/**
* Variant of {@link #PatternsRequestCondition(String...)} with a
* {@link UrlPathHelper} and a {@link PathMatcher}, and flags for matching
* with suffixes and trailing slashes, along with specific extensions.
* <p>As of 5.3 the path is obtained through the static method
* {@link UrlPathHelper#getResolvedLookupPath} and a {@code UrlPathHelper}
* does not need to be passed in.
* @deprecated as of 5.2.4. See class-level note in
* {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping}
* on the deprecation of path extension config options.
*/
@Deprecated
public PatternsRequestCondition(String[] patterns, @Nullable UrlPathHelper urlPathHelper,
@Nullable PathMatcher pathMatcher, boolean useSuffixPatternMatch,
boolean useTrailingSlashMatch, @Nullable List<String> fileExtensions) {
public PatternsRequestCondition(
String[] patterns, @Nullable UrlPathHelper urlPathHelper, @Nullable PathMatcher pathMatcher) {
this.patterns = initPatterns(patterns);
this.pathMatcher = pathMatcher != null ? pathMatcher : new AntPathMatcher();
this.useSuffixPatternMatch = useSuffixPatternMatch;
this.useTrailingSlashMatch = useTrailingSlashMatch;
if (fileExtensions != null) {
for (String fileExtension : fileExtensions) {
if (fileExtension.charAt(0) != '.') {
fileExtension = "." + fileExtension;
}
this.fileExtensions.add(fileExtension);
}
}
this.pathMatcher = (pathMatcher != null ? pathMatcher : new AntPathMatcher());
}
private static Set<String> initPatterns(String[] patterns) {
@@ -183,9 +112,6 @@ public class PatternsRequestCondition extends AbstractRequestCondition<PatternsR
private PatternsRequestCondition(Set<String> patterns, PatternsRequestCondition other) {
this.patterns = patterns;
this.pathMatcher = other.pathMatcher;
this.useSuffixPatternMatch = other.useSuffixPatternMatch;
this.useTrailingSlashMatch = other.useTrailingSlashMatch;
this.fileExtensions.addAll(other.fileExtensions);
}
@@ -314,29 +240,9 @@ public class PatternsRequestCondition extends AbstractRequestCondition<PatternsR
if (pattern.equals(lookupPath)) {
return pattern;
}
if (this.useSuffixPatternMatch) {
if (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) {
for (String extension : this.fileExtensions) {
if (this.pathMatcher.match(pattern + extension, lookupPath)) {
return pattern + extension;
}
}
}
else {
boolean hasSuffix = pattern.indexOf('.') != -1;
if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) {
return pattern + ".*";
}
}
}
if (this.pathMatcher.match(pattern, lookupPath)) {
return pattern;
}
if (this.useTrailingSlashMatch) {
if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath)) {
return pattern + "/";
}
}
return null;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.servlet.mvc.method;
import java.util.List;
import java.util.Set;
import jakarta.servlet.ServletRequest;
@@ -40,7 +39,6 @@ import org.springframework.web.servlet.mvc.condition.ProducesRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestConditionHolder;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.pattern.PathPattern;
@@ -702,7 +700,6 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
}
@Override
@SuppressWarnings("deprecation")
public RequestMappingInfo build() {
PathPatternsRequestCondition pathPatternsCondition = null;
@@ -718,10 +715,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
else {
patternsCondition = (ObjectUtils.isEmpty(this.paths) ?
EMPTY_PATTERNS :
new PatternsRequestCondition(
this.paths, null, this.options.pathMatcher,
this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
this.options.getFileExtensions()));
new PatternsRequestCondition(this.paths, null, this.options.pathMatcher));
}
ContentNegotiationManager manager = this.options.getContentNegotiationManager();
@@ -783,7 +777,6 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
}
@Override
@SuppressWarnings("deprecation")
public Builder paths(String... paths) {
PathPatternParser parser = this.options.getPatternParserToUse();
@@ -794,10 +787,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
else {
this.patternsCondition = (ObjectUtils.isEmpty(paths) ?
EMPTY_PATTERNS :
new PatternsRequestCondition(
paths, null, this.options.getPathMatcher(),
this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
this.options.getFileExtensions()));
new PatternsRequestCondition(paths, null, this.options.getPathMatcher()));
}
return this;
}
@@ -876,7 +866,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
*/
public static class BuilderConfiguration {
private static PathPatternParser defaultPatternParser = new PathPatternParser();
private static final PathPatternParser defaultPatternParser = new PathPatternParser();
@Nullable
@@ -885,12 +875,6 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
@Nullable
private PathMatcher pathMatcher;
private boolean trailingSlashMatch = false;
private boolean suffixPatternMatch = false;
private boolean registeredSuffixPatternMatch = false;
@Nullable
private ContentNegotiationManager contentNegotiationManager;
@@ -900,7 +884,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
* {@link AbstractHandlerMapping#setPatternParser(PathPatternParser)}.
* <p><strong>Note:</strong> This property is mutually exclusive with
* {@link #setPathMatcher(PathMatcher)}.
* <p>By default this is not set, but {@link RequestMappingInfo.Builder}
* <p>By default, this is not set, but {@link RequestMappingInfo.Builder}
* defaults to using {@link PathPatternParser} unless
* {@link #setPathMatcher(PathMatcher)} is explicitly set.
* @since 5.3
@@ -944,7 +928,7 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
/**
* Set a custom PathMatcher to use for the PatternsRequestCondition.
* <p>By default this is not set. You must set it explicitly if you want
* <p>By default, this is not set. You must set it explicitly if you want
* {@link PathMatcher} to be used, or otherwise {@link RequestMappingInfo}
* defaults to using {@link PathPatternParser}.
*/
@@ -974,97 +958,9 @@ public final class RequestMappingInfo implements RequestCondition<RequestMapping
return this.patternParser;
}
/**
* Set whether to apply trailing slash matching in PatternsRequestCondition.
* <p>The default was changed in 6.0 from {@code true} to {@code false} in
* order to support the deprecation of the property.
* @deprecated as of 6.0, see
* {@link PathPatternParser#setMatchOptionalTrailingSeparator(boolean)}
*/
@Deprecated(since = "6.0")
public void setTrailingSlashMatch(boolean trailingSlashMatch) {
this.trailingSlashMatch = trailingSlashMatch;
}
/**
* Return whether to apply trailing slash matching in PatternsRequestCondition.
* @deprecated as of 6.0 together with {@link #setTrailingSlashMatch(boolean)}
*/
@Deprecated(since = "6.0")
public boolean useTrailingSlashMatch() {
return this.trailingSlashMatch;
}
/**
* Set whether to apply suffix pattern matching in PatternsRequestCondition.
* <p>By default this is set to 'false'.
* @see #setRegisteredSuffixPatternMatch(boolean)
* @deprecated as of 5.2.4. See deprecation note on
* {@link RequestMappingHandlerMapping#setUseSuffixPatternMatch(boolean)}.
*/
@Deprecated
public void setSuffixPatternMatch(boolean suffixPatternMatch) {
this.suffixPatternMatch = suffixPatternMatch;
}
/**
* Return whether to apply suffix pattern matching in PatternsRequestCondition.
* @deprecated as of 5.2.4. See deprecation note on
* {@link RequestMappingHandlerMapping#setUseSuffixPatternMatch(boolean)}.
*/
@Deprecated
public boolean useSuffixPatternMatch() {
return this.suffixPatternMatch;
}
/**
* Set whether suffix pattern matching should be restricted to registered
* file extensions only. Setting this property also sets
* {@code suffixPatternMatch=true} and requires that a
* {@link #setContentNegotiationManager} is also configured in order to
* obtain the registered file extensions.
* @deprecated as of 5.2.4. See class-level note in
* {@link RequestMappingHandlerMapping} on the deprecation of path
* extension config options.
*/
@Deprecated
public void setRegisteredSuffixPatternMatch(boolean registeredSuffixPatternMatch) {
this.registeredSuffixPatternMatch = registeredSuffixPatternMatch;
this.suffixPatternMatch = (registeredSuffixPatternMatch || this.suffixPatternMatch);
}
/**
* Return whether suffix pattern matching should be restricted to registered
* file extensions only.
* @deprecated as of 5.2.4. See class-level note in
* {@link RequestMappingHandlerMapping} on the deprecation of path
* extension config options.
*/
@Deprecated
public boolean useRegisteredSuffixPatternMatch() {
return this.registeredSuffixPatternMatch;
}
/**
* Return the file extensions to use for suffix pattern matching. If
* {@code registeredSuffixPatternMatch=true}, the extensions are obtained
* from the configured {@code contentNegotiationManager}.
* @deprecated as of 5.2.4. See class-level note in
* {@link RequestMappingHandlerMapping} on the deprecation of path
* extension config options.
*/
@Nullable
@Deprecated
public List<String> getFileExtensions() {
if (useRegisteredSuffixPatternMatch() && this.contentNegotiationManager != null) {
return this.contentNegotiationManager.getAllFileExtensions();
}
return null;
}
/**
* Set the ContentNegotiationManager to use for the ProducesRequestCondition.
* <p>By default this is not set.
* <p>By default, this is not set.
*/
public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
this.contentNegotiationManager = contentNegotiationManager;

View File

@@ -59,22 +59,12 @@ import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import org.springframework.web.util.UrlPathHelper;
import org.springframework.web.util.pattern.PathPatternParser;
/**
* Creates {@link RequestMappingInfo} instances from type-level and method-level
* {@link RequestMapping @RequestMapping} and {@link HttpExchange @HttpExchange}
* annotations in {@link Controller @Controller} classes.
*
* <p><strong>Deprecation Note:</strong></p> In 5.2.4,
* {@link #setUseSuffixPatternMatch(boolean) useSuffixPatternMatch} and
* {@link #setUseRegisteredSuffixPatternMatch(boolean) useRegisteredSuffixPatternMatch}
* were deprecated in order to discourage use of path extensions for request
* mapping and for content negotiation (with similar deprecations in
* {@link org.springframework.web.accept.ContentNegotiationManagerFactoryBean
* ContentNegotiationManagerFactoryBean}). For further context, please read issue
* <a href="https://github.com/spring-projects/spring-framework/issues/24179">#24179</a>.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @author Sam Brannen
@@ -89,14 +79,6 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
private static final RequestMethod[] EMPTY_REQUEST_METHOD_ARRAY = new RequestMethod[0];
private boolean defaultPatternParser = true;
private boolean useSuffixPatternMatch = false;
private boolean useRegisteredSuffixPatternMatch = false;
private boolean useTrailingSlashMatch = false;
private Map<String, Predicate<Class<?>>> pathPrefixes = Collections.emptyMap();
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
@@ -107,65 +89,6 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
@Override
public void setPatternParser(@Nullable PathPatternParser patternParser) {
if (patternParser != null) {
this.defaultPatternParser = false;
}
super.setPatternParser(patternParser);
}
/**
* 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 value this is set to {@code false}.
* <p>Also see {@link #setUseRegisteredSuffixPatternMatch(boolean)} for
* more fine-grained control over specific suffixes to allow.
* <p><strong>Note:</strong> This property is ignored when
* {@link #setPatternParser(PathPatternParser)} is configured.
* @deprecated as of 5.2.4. See class level note on the deprecation of
* path extension config options. As there is no replacement for this method,
* in 5.2.x it is necessary to set it to {@code false}. In 5.3 the default
* changes to {@code false} and use of this property becomes unnecessary.
*/
@Deprecated
public void setUseSuffixPatternMatch(boolean useSuffixPatternMatch) {
this.useSuffixPatternMatch = useSuffixPatternMatch;
}
/**
* Whether suffix pattern matching should work only against path extensions
* explicitly registered with the {@link ContentNegotiationManager}. 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 "false".
* <p><strong>Note:</strong> This property is ignored when
* {@link #setPatternParser(PathPatternParser)} is configured.
* @deprecated as of 5.2.4. See class level note on the deprecation of
* path extension config options.
*/
@Deprecated
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 was changed in 6.0 from {@code true} to {@code false} in
* order to support the deprecation of the property.
* @deprecated as of 6.0, see
* {@link PathPatternParser#setMatchOptionalTrailingSeparator(boolean)}
*/
@Deprecated(since = "6.0")
public void setUseTrailingSlashMatch(boolean useTrailingSlashMatch) {
this.useTrailingSlashMatch = useTrailingSlashMatch;
if (getPatternParser() != null) {
getPatternParser().setMatchOptionalTrailingSeparator(useTrailingSlashMatch);
}
}
/**
* Configure path prefixes to apply to controller methods.
* <p>Prefixes are used to enrich the mappings of every {@code @RequestMapping}
@@ -216,23 +139,12 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
@SuppressWarnings("deprecation")
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setTrailingSlashMatch(useTrailingSlashMatch());
this.config.setContentNegotiationManager(getContentNegotiationManager());
if (getPatternParser() != null && this.defaultPatternParser &&
(this.useSuffixPatternMatch || this.useRegisteredSuffixPatternMatch)) {
setPatternParser(null);
}
if (getPatternParser() != null) {
this.config.setPatternParser(getPatternParser());
Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
"Suffix pattern matching not supported with PathPatternParser.");
}
else {
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setPathMatcher(getPathMatcher());
}
@@ -240,45 +152,6 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi
}
/**
* Whether to use registered suffixes for pattern matching.
* @deprecated as of 5.2.4. See deprecation notice on
* {@link #setUseSuffixPatternMatch(boolean)}.
*/
@Deprecated
public boolean useSuffixPatternMatch() {
return this.useSuffixPatternMatch;
}
/**
* Whether to use registered suffixes for pattern matching.
* @deprecated as of 5.2.4. See deprecation notice on
* {@link #setUseRegisteredSuffixPatternMatch(boolean)}.
*/
@Deprecated
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 file extensions to use for suffix pattern matching.
* @deprecated as of 5.2.4. See class-level note on the deprecation of path
* extension config options.
*/
@Nullable
@Deprecated
@SuppressWarnings("deprecation")
public List<String> getFileExtensions() {
return this.config.getFileExtensions();
}
/**
* Obtain a {@link RequestMappingInfo.BuilderConfiguration} that reflects
* the internal configuration of this {@code HandlerMapping} and can be used

View File

@@ -31,51 +31,6 @@
]]></xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:attribute name="suffix-pattern" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether to use suffix pattern match (".*") when matching patterns to requests. If enabled
a method mapped to "/users" also matches to "/users.*".
In 5.2.4, suffix pattern matching was deprecated in order to discourage use of path
extensions for request mapping and for content negotiation (see deprecation notes on
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping and on
org.springframework.web.accept.ContentNegotiationManagerFactoryBean and
also see https://github.com/spring-projects/spring-framework/issues/24179.
The default value is true but in 5.3 it changes to false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="trailing-slash" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether to match to URLs irrespective of the presence of a trailing slash.
If enabled a method mapped to "/users" also matches to "/users/".
The default was changed in 6.0 from true to false in order to support the
deprecation of the property.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="registered-suffixes-only" type="xsd:boolean">
<xsd:annotation>
<xsd:documentation><![CDATA[
Whether suffix pattern matching should work only against path extensions
explicitly registered when you configure content negotiation.
This is generally recommended to reduce ambiguity and to
avoid issues such as when a "." appears in the path for other reasons.
In 5.2.4, suffix pattern matching was deprecated in order to discourage use of path
extensions for request mapping and for content negotiation (see deprecation notes on
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping and on
org.springframework.web.accept.ContentNegotiationManagerFactoryBean and
also see https://github.com/spring-projects/spring-framework/issues/24179.
The default value is false.
]]></xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="path-helper" type="xsd:string">
<xsd:annotation>
<xsd:documentation><![CDATA[

View File

@@ -79,19 +79,13 @@ class AnnotationDrivenBeanDefinitionParserTests {
}
@Test
@SuppressWarnings("deprecation")
public void testPathMatchingConfiguration() {
loadBeanDefinitions("mvc-config-path-matching.xml");
RequestMappingHandlerMapping hm = this.appContext.getBean(RequestMappingHandlerMapping.class);
assertThat(hm).isNotNull();
assertThat(hm.useSuffixPatternMatch()).isTrue();
assertThat(hm.useTrailingSlashMatch()).isFalse();
assertThat(hm.useRegisteredSuffixPatternMatch()).isTrue();
assertThat(hm.getUrlPathHelper()).isInstanceOf(TestPathHelper.class);
assertThat(hm.getPathMatcher()).isInstanceOf(TestPathMatcher.class);
assertThat(hm.getPatternParser()).isNull();
List<String> fileExtensions = hm.getContentNegotiationManager().getAllFileExtensions();
assertThat(fileExtensions).containsExactly("xml");
}
@Test

View File

@@ -240,10 +240,7 @@ public class DelegatingWebMvcConfigurationTests {
@Override
@SuppressWarnings("deprecation")
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseRegisteredSuffixPatternMatch(true)
.setUseTrailingSlashMatch(false)
.setUrlPathHelper(pathHelper)
.setPathMatcher(pathMatcher);
configurer.setUrlPathHelper(pathHelper).setPathMatcher(pathMatcher);
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
@@ -272,9 +269,6 @@ public class DelegatingWebMvcConfigurationTests {
webMvcConfig.mvcResourceUrlProvider());
assertThat(annotationsMapping).isNotNull();
assertThat(annotationsMapping.useRegisteredSuffixPatternMatch()).isTrue();
assertThat(annotationsMapping.useSuffixPatternMatch()).isTrue();
assertThat(annotationsMapping.useTrailingSlashMatch()).isFalse();
configAssertion.accept(annotationsMapping.getUrlPathHelper(), annotationsMapping.getPathMatcher());
SimpleUrlHandlerMapping mapping = (SimpleUrlHandlerMapping) webMvcConfig.viewControllerHandlerMapping(

View File

@@ -16,8 +16,6 @@
package org.springframework.web.servlet.mvc.condition;
import java.util.Collections;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
@@ -111,88 +109,6 @@ class PatternsRequestConditionTests {
assertThat(match).isEqualTo(expected);
}
@Test
@SuppressWarnings("deprecation")
void matchSuffixPattern() {
MockHttpServletRequest request = initRequest("/foo.html");
boolean useSuffixPatternMatch = true;
PatternsRequestCondition condition =
new PatternsRequestCondition(new String[] {"/{foo}"}, null, null, useSuffixPatternMatch, true);
PatternsRequestCondition match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
assertThat(match.getPatterns()).containsExactly("/{foo}.*");
useSuffixPatternMatch = false;
condition = new PatternsRequestCondition(
new String[] {"/{foo}"}, null, null, useSuffixPatternMatch, false);
match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
assertThat(match.getPatterns()).containsExactly("/{foo}");
}
@Test // SPR-8410
@SuppressWarnings("deprecation")
void matchSuffixPatternUsingFileExtensions() {
PatternsRequestCondition condition = new PatternsRequestCondition(
new String[] {"/jobs/{jobName}"}, null, null, true, false, Collections.singletonList("json"));
MockHttpServletRequest request = initRequest("/jobs/my.job");
PatternsRequestCondition match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
assertThat(match.getPatterns()).containsExactly("/jobs/{jobName}");
request = initRequest("/jobs/my.job.json");
match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
assertThat(match.getPatterns()).containsExactly("/jobs/{jobName}.json");
}
@Test
@SuppressWarnings("deprecation")
void matchSuffixPatternUsingFileExtensions2() {
PatternsRequestCondition condition1 = new PatternsRequestCondition(
new String[] {"/prefix"}, null, null, true, false, Collections.singletonList("json"));
PatternsRequestCondition condition2 = new PatternsRequestCondition(
new String[] {"/suffix"}, null, null, true, false, null);
PatternsRequestCondition combined = condition1.combine(condition2);
MockHttpServletRequest request = initRequest("/prefix/suffix.json");
PatternsRequestCondition match = combined.getMatchingCondition(request);
assertThat(match).isNotNull();
}
@Test
void matchTrailingSlash() {
MockHttpServletRequest request = initRequest("/foo/");
PatternsRequestCondition condition = new PatternsRequestCondition("/foo");
PatternsRequestCondition match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
assertThat(match.getPatterns()).containsExactly("/foo/");
condition = new PatternsRequestCondition(new String[] {"/foo"}, true, null);
match = condition.getMatchingCondition(request);
assertThat(match).isNotNull();
assertThat(match.getPatterns()).first(STRING)
.as("Trailing slash should be insensitive to useSuffixPatternMatch settings (SPR-6164, SPR-5636)")
.isEqualTo("/foo/");
condition = new PatternsRequestCondition(new String[] {"/foo"}, false, null);
match = condition.getMatchingCondition(request);
assertThat(match).isNull();
}
@Test
void matchPatternContainsExtension() {
MockHttpServletRequest request = initRequest("/foo.html");
@@ -203,11 +119,7 @@ class PatternsRequestConditionTests {
@Test // gh-22543
void matchWithEmptyPatterns() {
PatternsRequestCondition condition = new PatternsRequestCondition();
assertThat(condition.getMatchingCondition(initRequest(""))).isNotNull();
assertThat(condition.getMatchingCondition(initRequest("/anything"))).isNull();
condition = condition.combine(new PatternsRequestCondition());
PatternsRequestCondition condition = new PatternsRequestCondition().combine(new PatternsRequestCondition());
assertThat(condition.getMatchingCondition(initRequest(""))).isNotNull();
assertThat(condition.getMatchingCondition(initRequest("/anything"))).isNull();
}

View File

@@ -616,15 +616,12 @@ class RequestMappingInfoHandlerMappingTests {
}
}
@SuppressWarnings("deprecation")
private RequestMappingInfo.BuilderConfiguration getBuilderConfig() {
RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
if (getPatternParser() != null) {
config.setPatternParser(getPatternParser());
}
else {
config.setSuffixPatternMatch(true);
config.setRegisteredSuffixPatternMatch(true);
config.setPathMatcher(getPathMatcher());
}
return config;

View File

@@ -23,8 +23,6 @@ import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
@@ -35,7 +33,6 @@ import org.springframework.core.annotation.AliasFor;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
@@ -102,74 +99,6 @@ class RequestMappingHandlerMappingTests {
assertThat(mapping.getBuilderConfiguration()).isNotNull().isNotSameAs(config);
}
@Test
@SuppressWarnings("deprecation")
void useRegisteredSuffixPatternMatch() {
RequestMappingHandlerMapping mapping = createMapping();
Map<String, MediaType> fileExtensions = Collections.singletonMap("json", MediaType.APPLICATION_JSON);
org.springframework.web.accept.PathExtensionContentNegotiationStrategy strategy =
new org.springframework.web.accept.PathExtensionContentNegotiationStrategy(fileExtensions);
ContentNegotiationManager manager = new ContentNegotiationManager(strategy);
mapping.setContentNegotiationManager(manager);
mapping.setUseRegisteredSuffixPatternMatch(true);
mapping.afterPropertiesSet();
assertThat(mapping.useSuffixPatternMatch()).isTrue();
assertThat(mapping.useRegisteredSuffixPatternMatch()).isTrue();
assertThat(mapping.getFileExtensions()).isEqualTo(Collections.singletonList("json"));
}
@Test
@SuppressWarnings("deprecation")
void useRegisteredSuffixPatternMatchInitialization() {
Map<String, MediaType> fileExtensions = Collections.singletonMap("json", MediaType.APPLICATION_JSON);
org.springframework.web.accept.PathExtensionContentNegotiationStrategy strategy =
new org.springframework.web.accept.PathExtensionContentNegotiationStrategy(fileExtensions);
ContentNegotiationManager manager = new ContentNegotiationManager(strategy);
final Set<String> extensions = new HashSet<>();
RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping() {
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
extensions.addAll(getFileExtensions());
return super.getMappingForMethod(method, handlerType);
}
};
StaticWebApplicationContext wac = new StaticWebApplicationContext();
wac.registerSingleton("testController", ComposedAnnotationController.class);
wac.refresh();
mapping.setContentNegotiationManager(manager);
mapping.setUseRegisteredSuffixPatternMatch(true);
mapping.setApplicationContext(wac);
mapping.afterPropertiesSet();
assertThat(extensions).containsOnly("json");
}
@Test
@SuppressWarnings("deprecation")
void suffixPatternMatchSettings() {
RequestMappingHandlerMapping mapping = createMapping();
assertThat(mapping.useSuffixPatternMatch()).isFalse();
assertThat(mapping.useRegisteredSuffixPatternMatch()).isFalse();
mapping.setUseRegisteredSuffixPatternMatch(false);
assertThat(mapping.useSuffixPatternMatch())
.as("'false' registeredSuffixPatternMatch shouldn't impact suffixPatternMatch")
.isFalse();
mapping.setUseRegisteredSuffixPatternMatch(true);
assertThat(mapping.useSuffixPatternMatch())
.as("'true' registeredSuffixPatternMatch should enable suffixPatternMatch")
.isTrue();
}
@PathPatternsParameterizedTest
void resolveEmbeddedValuesInPatterns(RequestMappingHandlerMapping mapping) {
mapping.setEmbeddedValueResolver(value -> "/${pattern}/bar".equals(value) ? "/foo/bar" : value);

View File

@@ -178,15 +178,7 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
@PathPatternsParameterizedTest
void emptyValueMapping(boolean usePathPatterns) throws Exception {
initDispatcherServlet(ControllerWithEmptyValueMapping.class, usePathPatterns, wac -> {
if (!usePathPatterns) {
// UrlPathHelper returns "/" for "",
// so either the mapping has to be "/" or trailingSlashMatch must be on
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useTrailingSlashMatch", true);
wac.registerBeanDefinition("handlerMapping", mappingDef);
}
});
initDispatcherServlet(ControllerWithEmptyValueMapping.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.setContextPath("/foo");
@@ -207,15 +199,7 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
@PathPatternsParameterizedTest
void errorThrownFromHandlerMethod(boolean usePathPatterns) throws Exception {
initDispatcherServlet(ControllerWithErrorThrown.class, usePathPatterns, wac -> {
if (!usePathPatterns) {
// UrlPathHelper returns "/" for "",
// so either the mapping has to be "/" or trailingSlashMatch must be on
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useTrailingSlashMatch", true);
wac.registerBeanDefinition("handlerMapping", mappingDef);
}
});
initDispatcherServlet(ControllerWithErrorThrown.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo");
request.setContextPath("/foo");
@@ -514,13 +498,7 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
@PathPatternsParameterizedTest
void adaptedHandleMethods(boolean usePathPatterns) throws Exception {
initDispatcherServlet(MyAdaptedController.class, usePathPatterns, wac -> {
if (!usePathPatterns) {
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useSuffixPatternMatch", true);
wac.registerBeanDefinition("handlerMapping", mappingDef);
}
});
initDispatcherServlet(MyAdaptedController.class, usePathPatterns);
doTestAdaptedHandleMethods(usePathPatterns);
}
@@ -560,12 +538,6 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
response = new MockHttpServletResponse();
getServlet().service(request, response);
if (!usePathPatterns) {
// This depends on suffix pattern matching and has different outcomes otherwise,
// for example, 404 vs another method matching, depending on the test case.
assertThat(response.getContentAsString()).isEqualTo("test-name1-2");
}
request = new MockHttpServletRequest("GET", "/myPath4.do");
request.addParameter("param1", "value1");
request.addParameter("param2", "2");
@@ -787,13 +759,7 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
@PathPatternsParameterizedTest
void relativePathDispatchingController(boolean usePathPatterns) throws Exception {
initDispatcherServlet(MyRelativePathDispatchingController.class, usePathPatterns, wac -> {
if (!usePathPatterns) {
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useSuffixPatternMatch", true);
wac.registerBeanDefinition("handlerMapping", mappingDef);
}
});
initDispatcherServlet(MyRelativePathDispatchingController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myHandle");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -809,22 +775,11 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
response = new MockHttpServletResponse();
getServlet().service(request, response);
assertThat(response.getContentAsString()).isEqualTo("myLangView");
request = new MockHttpServletRequest("GET", "/myApp/surprise.do");
response = new MockHttpServletResponse();
getServlet().service(request, response);
assertThat(response.getContentAsString()).isEqualTo(!usePathPatterns ? "mySurpriseView" : "myView");
}
@PathPatternsParameterizedTest
void relativeMethodPathDispatchingController(boolean usePathPatterns) throws Exception {
initDispatcherServlet(MyRelativeMethodPathDispatchingController.class, usePathPatterns, wac -> {
if (!usePathPatterns) {
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useSuffixPatternMatch", true);
wac.registerBeanDefinition("handlerMapping", mappingDef);
}
});
initDispatcherServlet(MyRelativeMethodPathDispatchingController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myHandle");
MockHttpServletResponse response = new MockHttpServletResponse();
@@ -845,15 +800,7 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
response = new MockHttpServletResponse();
getServlet().service(request, response);
if (!usePathPatterns) {
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.getContentAsString()).isEqualTo("mySurpriseView");
}
else {
assertThat(response.getStatus())
.as("Suffixes pattern matching should not work with PathPattern's")
.isEqualTo(404);
}
assertThat(response.getStatus()).as("Suffixes pattern matching is not supported").isEqualTo(404);
}
@PathPatternsParameterizedTest
@@ -1776,108 +1723,6 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
assertThat(response.getContentLength()).as("Expected an empty content").isEqualTo(0);
}
@PathPatternsParameterizedTest
@SuppressWarnings("deprecation")
void responseBodyAsHtml(boolean usePathPatterns) throws Exception {
initDispatcherServlet(TextRestController.class, usePathPatterns, wac -> {
if (!usePathPatterns) {
// `useSuffixPatternMatch` is not allowed with PathPattern's
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useSuffixPatternMatch", true);
wac.registerBeanDefinition("handlerMapping", mappingDef);
}
ContentNegotiationManagerFactoryBean factoryBean = new ContentNegotiationManagerFactoryBean();
factoryBean.setFavorPathExtension(true);
factoryBean.afterPropertiesSet();
RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
adapterDef.getPropertyValues().add("contentNegotiationManager", factoryBean.getObject());
wac.registerBeanDefinition("handlerAdapter", adapterDef);
});
byte[] content = "alert('boo')".getBytes(StandardCharsets.ISO_8859_1);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/a1.html");
request.setContent(content);
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
if (!usePathPatterns) {
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.getContentType()).isEqualTo("text/html;charset=ISO-8859-1");
assertThat(response.getHeader("Content-Disposition")).isEqualTo("inline;filename=f.txt");
assertThat(response.getContentAsByteArray()).isEqualTo(content);
}
else {
assertThat(response.getStatus())
.as("Suffixes pattern matching should not work with PathPattern's")
.isEqualTo(404);
}
}
@PathPatternsParameterizedTest
@SuppressWarnings("deprecation")
void responseBodyAsHtmlWithSuffixPresent(boolean usePathPatterns) throws Exception {
initDispatcherServlet(TextRestController.class, usePathPatterns, wac -> {
ContentNegotiationManagerFactoryBean factoryBean = new ContentNegotiationManagerFactoryBean();
factoryBean.setFavorPathExtension(true);
factoryBean.afterPropertiesSet();
RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
adapterDef.getPropertyValues().add("contentNegotiationManager", factoryBean.getObject());
wac.registerBeanDefinition("handlerAdapter", adapterDef);
});
byte[] content = "alert('boo')".getBytes(StandardCharsets.ISO_8859_1);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/a2.html");
request.setContent(content);
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.getContentType()).isEqualTo("text/html;charset=ISO-8859-1");
assertThat(response.getHeader("Content-Disposition")).isNull();
assertThat(response.getContentAsByteArray()).isEqualTo(content);
}
@PathPatternsParameterizedTest
void responseBodyAsHtmlWithProducesCondition(boolean usePathPatterns) throws Exception {
initDispatcherServlet(TextRestController.class, usePathPatterns, wac -> {
if (!usePathPatterns) {
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useSuffixPatternMatch", true);
wac.registerBeanDefinition("handlerMapping", mappingDef);
}
ContentNegotiationManagerFactoryBean factoryBean = new ContentNegotiationManagerFactoryBean();
factoryBean.afterPropertiesSet();
RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
adapterDef.getPropertyValues().add("contentNegotiationManager", factoryBean.getObject());
wac.registerBeanDefinition("handlerAdapter", adapterDef);
});
byte[] content = "alert('boo')".getBytes(StandardCharsets.ISO_8859_1);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/a3.html");
request.setContent(content);
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
if (!usePathPatterns) {
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.getContentType()).isEqualTo("text/html;charset=ISO-8859-1");
assertThat(response.getHeader("Content-Disposition")).isNull();
assertThat(response.getContentAsByteArray()).isEqualTo(content);
}
else {
assertThat(response.getStatus())
.as("Suffixes pattern matching should not work with PathPattern's")
.isEqualTo(404);
}
}
@PathPatternsParameterizedTest
void responseBodyAsTextWithCssExtension(boolean usePathPatterns) throws Exception {
initDispatcherServlet(TextRestController.class, usePathPatterns, wac -> {
@@ -3052,7 +2897,6 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
static class TestViewResolver implements ViewResolver {
@SuppressWarnings("deprecation")
@Override
public View resolveViewName(final String viewName, Locale locale) throws Exception {
return (model, request, response) -> {
@@ -4021,16 +3865,6 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
return body;
}
@RequestMapping(path = "/a2.html", method = RequestMethod.GET)
public String a2(@RequestBody String body) {
return body;
}
@RequestMapping(path = "/a3", method = RequestMethod.GET, produces = "text/html")
public String a3(@RequestBody String body) throws IOException {
return body;
}
@RequestMapping(path = "/a4", method = RequestMethod.GET)
public String a4(@RequestBody String body) {
return body;

View File

@@ -173,7 +173,6 @@ class UriTemplateServletAnnotationControllerHandlerMethodTests extends AbstractS
initDispatcherServlet(SimpleUriTemplateController.class, usePathPatterns, wac -> {
if (!usePathPatterns) {
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useSuffixPatternMatch", true);
mappingDef.getPropertyValues().add("removeSemicolonContent", "false");
wac.registerBeanDefinition("handlerMapping", mappingDef);
}
@@ -182,8 +181,7 @@ class UriTemplateServletAnnotationControllerHandlerMethodTests extends AbstractS
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/42;jsessionid=c0o7fszeb1;q=24.xml");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertThat(response.getContentAsString())
.isEqualTo(!usePathPatterns ? "test-42-24" : "test-42-24.xml");
assertThat(response.getContentAsString()).isEqualTo("test-42-24.xml");
}
@PathPatternsParameterizedTest
@@ -320,18 +318,12 @@ class UriTemplateServletAnnotationControllerHandlerMethodTests extends AbstractS
@PathPatternsParameterizedTest // gh-13187
void variableNamesWithUrlExtension(boolean usePathPatterns) throws Exception {
initDispatcherServlet(VariableNamesController.class, usePathPatterns, wac -> {
if (!usePathPatterns) {
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
mappingDef.getPropertyValues().add("useSuffixPatternMatch", true);
wac.registerBeanDefinition("handlerMapping", mappingDef);
}
});
initDispatcherServlet(VariableNamesController.class, usePathPatterns);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test/foo.json");
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertThat(response.getContentAsString()).isEqualTo(!usePathPatterns ? "foo-foo" : "foo-foo.json");
assertThat(response.getContentAsString()).isEqualTo("foo-foo.json");
}
@PathPatternsParameterizedTest // gh-11643
@@ -683,13 +675,4 @@ class UriTemplateServletAnnotationControllerHandlerMethodTests extends AbstractS
}
}
// @Disabled("ControllerClassNameHandlerMapping")
// void controllerClassName() throws Exception {
// @Disabled("useDefaultSuffixPattern property not supported")
// void doubles() throws Exception {
// @Disabled("useDefaultSuffixPattern property not supported")
// void noDefaultSuffixPattern() throws Exception {
}

View File

@@ -149,10 +149,8 @@ class ResourceHttpRequestHandlerTests {
}
@Test // SPR-14577
@SuppressWarnings("deprecation")
void getMediaTypeWithFavorPathExtensionOff() throws Exception {
ContentNegotiationManagerFactoryBean factory = new ContentNegotiationManagerFactoryBean();
factory.setFavorPathExtension(false);
factory.afterPropertiesSet();
ContentNegotiationManager manager = factory.getObject();

View File

@@ -6,12 +6,7 @@
http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<mvc:path-matching
suffix-pattern="true"
trailing-slash="false"
registered-suffixes-only="true"
path-helper="pathHelper"
path-matcher="pathMatcher" />
<mvc:path-matching path-helper="pathHelper" path-matcher="pathMatcher" />
</mvc:annotation-driven>
<bean id="pathMatcher" class="org.springframework.web.servlet.config.TestPathMatcher" />