diff --git a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java index 0d4a1f7dd8..f39d1b4a2d 100644 --- a/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java +++ b/spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManagerFactoryBean.java @@ -50,18 +50,18 @@ import org.springframework.web.context.ServletContextAware; * Enabled Or Not * * - * {@link #setFavorPathExtension favorPathExtension} - * true - * {@link PathExtensionContentNegotiationStrategy} - * Enabled - * - * * {@link #setFavorParameter favorParameter} * false * {@link ParameterContentNegotiationStrategy} * Off * * + * {@link #setFavorPathExtension favorPathExtension} + * false (as of 5.3) + * {@link PathExtensionContentNegotiationStrategy} + * Off + * + * * {@link #setIgnoreAcceptHeader ignoreAcceptHeader} * false * {@link HeaderContentNegotiationStrategy} @@ -104,11 +104,11 @@ public class ContentNegotiationManagerFactoryBean private List strategies; - private boolean favorPathExtension = true; - private boolean favorParameter = false; - private boolean ignoreAcceptHeader = false; + private String parameterName = "format"; + + private boolean favorPathExtension = true; private Map mediaTypes = new HashMap<>(); @@ -117,7 +117,7 @@ public class ContentNegotiationManagerFactoryBean @Nullable private Boolean useRegisteredExtensionsOnly; - private String parameterName = "format"; + private boolean ignoreAcceptHeader = false; @Nullable private ContentNegotiationStrategy defaultNegotiationStrategy; @@ -141,17 +141,35 @@ public class ContentNegotiationManagerFactoryBean this.strategies = (strategies != null ? new ArrayList<>(strategies) : null); } + /** + * Whether a request parameter ("format" by default) should be used to + * determine the requested media type. For this option to work you must + * register {@link #setMediaTypes media type mappings}. + *

By default this is set to {@code false}. + * @see #setParameterName + */ + public void setFavorParameter(boolean favorParameter) { + this.favorParameter = favorParameter; + } + + /** + * Set the query parameter name to use when {@link #setFavorParameter} is on. + *

The default parameter name is {@code "format"}. + */ + public void setParameterName(String parameterName) { + Assert.notNull(parameterName, "parameterName is required"); + this.parameterName = parameterName; + } + /** * Whether the path extension in the URL path should be used to determine * the requested media type. - *

By default this is set to {@code true} in which case a request - * for {@code /hotels.pdf} will be interpreted as a request for - * {@code "application/pdf"} regardless of the 'Accept' header. + *

By default this is set to {@code false} in which case path extensions + * have no impact on content negotiation. * @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, - * for the time being it's necessary to continue using it in order to set it - * to {@code false}. In 5.3 when {@code false} becomes the default, use of - * this property will no longer be necessary. + * in 5.2.x it is necessary to set it to {@code false}. In 5.3 {@code false} + * becomes the default, and use of this property is longer be necessary. */ @Deprecated public void setFavorPathExtension(boolean favorPathExtension) { @@ -224,8 +242,8 @@ public class ContentNegotiationManagerFactoryBean /** * Indicate whether to use the Java Activation Framework as a fallback option * to map from file extensions to media types. - * @deprecated as of 5.0, in favor of {@link #setUseRegisteredExtensionsOnly(boolean)}, which - * has reverse behavior. + * @deprecated as of 5.0, in favor of {@link #setUseRegisteredExtensionsOnly(boolean)}, + * which has reverse behavior. */ @Deprecated public void setUseJaf(boolean useJaf) { @@ -247,26 +265,6 @@ public class ContentNegotiationManagerFactoryBean return (this.useRegisteredExtensionsOnly != null && this.useRegisteredExtensionsOnly); } - /** - * Whether a request parameter ("format" by default) should be used to - * determine the requested media type. For this option to work you must - * register {@link #setMediaTypes media type mappings}. - *

By default this is set to {@code false}. - * @see #setParameterName - */ - public void setFavorParameter(boolean favorParameter) { - this.favorParameter = favorParameter; - } - - /** - * Set the query parameter name to use when {@link #setFavorParameter} is on. - *

The default parameter name is {@code "format"}. - */ - public void setParameterName(String parameterName) { - Assert.notNull(parameterName, "parameterName is required"); - this.parameterName = parameterName; - } - /** * Whether to disable checking the 'Accept' request header. *

By default this value is set to {@code false}. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java index 91217a1d5d..d1f606c9d0 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/ContentNegotiationConfigurer.java @@ -49,19 +49,19 @@ import org.springframework.web.accept.ParameterContentNegotiationStrategy; * Enabled Or Not * * - * {@link #favorPathExtension} - * true - * {@link org.springframework.web.accept.PathExtensionContentNegotiationStrategy - * PathExtensionContentNegotiationStrategy} - * Enabled - * - * * {@link #favorParameter} * false * {@link ParameterContentNegotiationStrategy} * Off * * + * {@link #favorPathExtension} + * false (as of 5.3) + * {@link org.springframework.web.accept.PathExtensionContentNegotiationStrategy + * PathExtensionContentNegotiationStrategy} + * Off + * + * * {@link #ignoreAcceptHeader} * false * {@link HeaderContentNegotiationStrategy} @@ -123,18 +123,34 @@ public class ContentNegotiationConfigurer { this.factory.setStrategies(strategies); } + /** + * Whether a request parameter ("format" by default) should be used to + * determine the requested media type. For this option to work you must + * register {@link #mediaType(String, MediaType) media type mappings}. + *

By default this is set to {@code false}. + * @see #parameterName(String) + */ + public ContentNegotiationConfigurer favorParameter(boolean favorParameter) { + this.factory.setFavorParameter(favorParameter); + return this; + } + + /** + * Set the query parameter name to use when {@link #favorParameter} is on. + *

The default parameter name is {@code "format"}. + */ + public ContentNegotiationConfigurer parameterName(String parameterName) { + this.factory.setParameterName(parameterName); + return this; + } + /** * Whether the path extension in the URL path should be used to determine * the requested media type. - *

By default this is set to {@code true} in which case a request - * for {@code /hotels.pdf} will be interpreted as a request for - * {@code "application/pdf"} regardless of the 'Accept' header. - * @deprecated as of 5.2.4. See class-level note in - * {@link ContentNegotiationManagerFactoryBean} on the deprecation of path - * extension config options. As there is no replacement for this method, - * for the time being it's necessary to continue using it in order to set it - * to {@code false}. In 5.3 when {@code false} becomes the default, use of - * this property will no longer be necessary. + *

By default this is set to {@code false} in which case path extensions + * have no impact on content negotiation. + * @deprecated as of 5.2.4. See deprecation note on + * {@link ContentNegotiationManagerFactoryBean#setFavorPathExtension(boolean)}. */ @Deprecated public ContentNegotiationConfigurer favorPathExtension(boolean favorPathExtension) { @@ -190,9 +206,8 @@ public class ContentNegotiationConfigurer { * to any media type. Setting this to {@code false} will result in an * {@code HttpMediaTypeNotAcceptableException} if there is no match. *

By default this is set to {@code true}. - * @deprecated as of 5.2.4. See class-level note in - * {@link ContentNegotiationManagerFactoryBean} on the deprecation of path - * extension config options. + * @deprecated as of 5.2.4. See deprecation note on + * {@link ContentNegotiationManagerFactoryBean#setIgnoreUnknownPathExtensions(boolean)}. */ @Deprecated public ContentNegotiationConfigurer ignoreUnknownPathExtensions(boolean ignore) { @@ -224,27 +239,6 @@ public class ContentNegotiationConfigurer { return this; } - /** - * Whether a request parameter ("format" by default) should be used to - * determine the requested media type. For this option to work you must - * register {@link #mediaType(String, MediaType) media type mappings}. - *

By default this is set to {@code false}. - * @see #parameterName(String) - */ - public ContentNegotiationConfigurer favorParameter(boolean favorParameter) { - this.factory.setFavorParameter(favorParameter); - return this; - } - - /** - * Set the query parameter name to use when {@link #favorParameter} is on. - *

The default parameter name is {@code "format"}. - */ - public ContentNegotiationConfigurer parameterName(String parameterName) { - this.factory.setParameterName(parameterName); - return this; - } - /** * Whether to disable checking the 'Accept' request header. *

By default this value is set to {@code false}. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java index 0cb6889960..d07e7731f4 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/PathMatchConfigurer.java @@ -69,9 +69,9 @@ public class PathMatchConfigurer { * @see #registeredSuffixPatternMatch * @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, for the time - * being it's necessary to set it to {@code false}. In 5.3 when {@code false} - * becomes the default, use of this property will no longer be necessary. + * 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 {@code false} becomes the + * default, and use of this property is longer be necessary. */ @Deprecated public PathMatchConfigurer setUseSuffixPatternMatch(Boolean suffixPatternMatch) { @@ -150,9 +150,8 @@ public class PathMatchConfigurer { /** * Whether to use registered suffixes for pattern matching. - * @deprecated as of 5.2.4. See class-level note in - * {@link RequestMappingHandlerMapping} on the deprecation of path extension - * config options. + * @deprecated as of 5.2.4, see deprecation note on + * {@link #setUseSuffixPatternMatch(Boolean)}. */ @Nullable @Deprecated @@ -162,9 +161,8 @@ public class PathMatchConfigurer { /** * Whether to use registered suffixes for pattern matching. - * @deprecated as of 5.2.4. See class-level note in - * {@link RequestMappingHandlerMapping} on the deprecation of path extension - * config options. + * @deprecated as of 5.2.4, see deprecation note on + * {@link #setUseRegisteredSuffixPatternMatch(Boolean)}. */ @Nullable @Deprecated diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java index 7d72839996..58a33ed8c8 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/RequestMappingInfo.java @@ -543,7 +543,7 @@ public final class RequestMappingInfo implements RequestConditionBy default this is set to 'true'. + *

By default this is set to 'false'. * @see #setRegisteredSuffixPatternMatch(boolean) - * @deprecated as of 5.2.4. See class-level note in - * {@link RequestMappingHandlerMapping} on the deprecation of path - * extension config options. + * @deprecated as of 5.2.4. See deprecation note on + * {@link RequestMappingHandlerMapping#setUseSuffixPatternMatch(boolean)}. */ @Deprecated public void setSuffixPatternMatch(boolean suffixPatternMatch) { @@ -613,9 +612,8 @@ public final class RequestMappingInfo implements RequestConditionDeprecation Note:

In 5.2.4, * {@link #setUseSuffixPatternMatch(boolean) useSuffixPatternMatch} and * {@link #setUseRegisteredSuffixPatternMatch(boolean) useRegisteredSuffixPatternMatch} - * are deprecated in order to discourage use of path extensions for request + * were deprecated in order to discourage use of path extensions for request * mapping and for content negotiation (with similar deprecations in * {@link ContentNegotiationManager}). For further context, please read issue * #24719. @@ -74,7 +74,7 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMappi public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping implements MatchableHandlerMapping, EmbeddedValueResolverAware { - private boolean useSuffixPatternMatch = true; + private boolean useSuffixPatternMatch = false; private boolean useRegisteredSuffixPatternMatch = false; @@ -93,14 +93,13 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi /** * Whether to use suffix pattern match (".*") when matching patterns to * requests. If enabled a method mapped to "/users" also matches to "/users.*". - *

The default value is {@code true}. + *

By default value this is set to {@code false}. *

Also see {@link #setUseRegisteredSuffixPatternMatch(boolean)} for * more fine-grained control over specific suffixes to allow. - * @deprecated as of 5.2.4. See class level comment about deprecation of + * @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, - * for the time being it's necessary to set it to {@code false}. In 5.3 - * when {@code false} becomes the default, use of this property will no - * longer be necessary. + * in 5.2.x it is necessary to set it to {@code false}. In 5.3 {@code false} + * becomes the default, and use of this property is longer be necessary. */ @Deprecated public void setUseSuffixPatternMatch(boolean useSuffixPatternMatch) { @@ -113,7 +112,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi * is generally recommended to reduce ambiguity and to avoid issues such as * when a "." appears in the path for other reasons. *

By default this is set to "false". - * @deprecated as of 5.2.4. See class level comment about deprecation of + * @deprecated as of 5.2.4. See class level note on the deprecation of * path extension config options. */ @Deprecated @@ -191,8 +190,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi /** * Whether to use registered suffixes for pattern matching. - * @deprecated as of 5.2.4. See class-level note on the deprecation of path - * extension config options. + * @deprecated as of 5.2.4. See deprecation notice on + * {@link #setUseSuffixPatternMatch(boolean)}. */ @Deprecated public boolean useSuffixPatternMatch() { @@ -201,8 +200,8 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi /** * Whether to use registered suffixes for pattern matching. - * @deprecated as of 5.2.4. See class-level note on the deprecation of path - * extension config options. + * @deprecated as of 5.2.4. See deprecation notice on + * {@link #setUseRegisteredSuffixPatternMatch(boolean)}. */ @Deprecated public boolean useRegisteredSuffixPatternMatch() { diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java index 3a5b7a2fce..356828e0b0 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMappingTests.java @@ -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. @@ -22,7 +22,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; import java.security.Principal; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Map; @@ -72,7 +71,7 @@ public class RequestMappingHandlerMappingTests { @Test public void useRegisteredSuffixPatternMatch() { - assertThat(this.handlerMapping.useSuffixPatternMatch()).isTrue(); + assertThat(this.handlerMapping.useSuffixPatternMatch()).isFalse(); assertThat(this.handlerMapping.useRegisteredSuffixPatternMatch()).isFalse(); Map fileExtensions = Collections.singletonMap("json", MediaType.APPLICATION_JSON); @@ -85,7 +84,7 @@ public class RequestMappingHandlerMappingTests { assertThat(this.handlerMapping.useSuffixPatternMatch()).isTrue(); assertThat(this.handlerMapping.useRegisteredSuffixPatternMatch()).isTrue(); - assertThat(this.handlerMapping.getFileExtensions()).isEqualTo(Arrays.asList("json")); + assertThat(this.handlerMapping.getFileExtensions()).isEqualTo(Collections.singletonList("json")); } @Test @@ -117,9 +116,6 @@ public class RequestMappingHandlerMappingTests { @Test public void useSuffixPatternMatch() { - assertThat(this.handlerMapping.useSuffixPatternMatch()).isTrue(); - - this.handlerMapping.setUseSuffixPatternMatch(false); assertThat(this.handlerMapping.useSuffixPatternMatch()).isFalse(); this.handlerMapping.setUseRegisteredSuffixPatternMatch(false); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java index cf9397a5a6..cdbdc47037 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java @@ -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. @@ -469,22 +469,26 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl @Test public void adaptedHandleMethods() throws Exception { - doTestAdaptedHandleMethods(MyAdaptedController.class); + initServlet(wac -> { + RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class); + mappingDef.getPropertyValues().add("useSuffixPatternMatch", true); + wac.registerBeanDefinition("handlerMapping", mappingDef); + }, MyAdaptedController.class); + doTestAdaptedHandleMethods(); } @Test public void adaptedHandleMethods2() throws Exception { - doTestAdaptedHandleMethods(MyAdaptedController2.class); + initServletWithControllers(MyAdaptedController2.class); } @Test public void adaptedHandleMethods3() throws Exception { - doTestAdaptedHandleMethods(MyAdaptedController3.class); + initServletWithControllers(MyAdaptedController3.class); + doTestAdaptedHandleMethods(); } - private void doTestAdaptedHandleMethods(final Class controllerClass) throws Exception { - initServletWithControllers(controllerClass); - + private void doTestAdaptedHandleMethods() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myPath1.do"); MockHttpServletResponse response = new MockHttpServletResponse(); request.addParameter("param1", "value1"); @@ -727,8 +731,11 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl @Test public void relativePathDispatchingController() throws Exception { - initServletWithControllers(MyRelativePathDispatchingController.class); - getServlet().init(new MockServletConfig()); + initServlet(wac -> { + RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class); + mappingDef.getPropertyValues().add("useSuffixPatternMatch", true); + wac.registerBeanDefinition("handlerMapping", mappingDef); + }, MyRelativePathDispatchingController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myHandle"); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -753,8 +760,11 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl @Test public void relativeMethodPathDispatchingController() throws Exception { - initServletWithControllers(MyRelativeMethodPathDispatchingController.class); - getServlet().init(new MockServletConfig()); + initServlet(wac -> { + RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class); + mappingDef.getPropertyValues().add("useSuffixPatternMatch", true); + wac.registerBeanDefinition("handlerMapping", mappingDef); + }, MyRelativeMethodPathDispatchingController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myHandle"); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -1674,8 +1684,13 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl @Test public void responseBodyAsHtml() throws Exception { initServlet(wac -> { + 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); @@ -1720,8 +1735,13 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl @Test public void responseBodyAsHtmlWithProducesCondition() throws Exception { initServlet(wac -> { + 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); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/UriTemplateServletAnnotationControllerHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/UriTemplateServletAnnotationControllerHandlerMethodTests.java index ceaf509939..ca6bdcf44e 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/UriTemplateServletAnnotationControllerHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/UriTemplateServletAnnotationControllerHandlerMethodTests.java @@ -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. @@ -34,7 +34,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.propertyeditors.CustomDateEditor; -import org.springframework.context.ApplicationContextInitializer; import org.springframework.stereotype.Controller; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.WebDataBinder; @@ -44,7 +43,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.view.AbstractView; @@ -87,15 +85,11 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab pathVars.put("booking", 21); pathVars.put("other", "other"); - WebApplicationContext wac = - initServlet(new ApplicationContextInitializer() { - @Override - public void initialize(GenericWebApplicationContext context) { - RootBeanDefinition beanDef = new RootBeanDefinition(ModelValidatingViewResolver.class); - beanDef.getConstructorArgumentValues().addGenericArgumentValue(pathVars); - context.registerBeanDefinition("viewResolver", beanDef); - } - }, ViewRenderingController.class); + WebApplicationContext wac = initServlet(context -> { + RootBeanDefinition beanDef = new RootBeanDefinition(ModelValidatingViewResolver.class); + beanDef.getConstructorArgumentValues().addGenericArgumentValue(pathVars); + context.registerBeanDefinition("viewResolver", beanDef); + }, ViewRenderingController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/hotels/42;q=1,2/bookings/21-other;q=3;r=R"); getServlet().service(request, new MockHttpServletResponse()); @@ -143,22 +137,21 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab MockHttpServletResponse response = new MockHttpServletResponse(); getServlet().service(request, response); assertThat(response.getContentAsString()).isEqualTo("test-42-21"); - - request = new MockHttpServletRequest("GET", "/hotels/42/bookings/21.html"); - response = new MockHttpServletResponse(); - getServlet().service(request, response); - assertThat(response.getContentAsString()).isEqualTo("test-42-21"); } @Test public void extension() throws Exception { - initServletWithControllers(SimpleUriTemplateController.class); + initServlet(wac -> { + RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class); + mappingDef.getPropertyValues().add("useSuffixPatternMatch", true); + mappingDef.getPropertyValues().add("removeSemicolonContent", "false"); + wac.registerBeanDefinition("handlerMapping", mappingDef); + }, SimpleUriTemplateController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/42;jsessionid=c0o7fszeb1;q=24.xml"); MockHttpServletResponse response = new MockHttpServletResponse(); getServlet().service(request, response); assertThat(response.getContentAsString()).isEqualTo("test-42-24"); - } @Test @@ -264,11 +257,6 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab MockHttpServletResponse response = new MockHttpServletResponse(); getServlet().service(request, response); assertThat(response.getContentAsString()).isEqualTo("handle4-page-5"); - - request = new MockHttpServletRequest("GET", "/category/page/5.html"); - response = new MockHttpServletResponse(); - getServlet().service(request, response); - assertThat(response.getContentAsString()).isEqualTo("handle4-page-5"); } @Test @@ -282,10 +270,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab assertThat(response.getContentAsString()).isEqualTo("test-42-;q=1;q=2-[1, 2]"); } - /* - * See SPR-6640 - */ - @Test + @Test // gh-11306 public void menuTree() throws Exception { initServletWithControllers(MenuTreeController.class); @@ -295,10 +280,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab assertThat(response.getContentAsString()).isEqualTo("M5"); } - /* - * See SPR-6876 - */ - @Test + @Test // gh-11542 public void variableNames() throws Exception { initServletWithControllers(VariableNamesController.class); @@ -313,12 +295,13 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab assertThat(response.getContentAsString()).isEqualTo("bar-bar"); } - /* - * See SPR-8543 - */ - @Test + @Test // gh-13187 public void variableNamesWithUrlExtension() throws Exception { - initServletWithControllers(VariableNamesController.class); + initServlet(wac -> { + RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class); + mappingDef.getPropertyValues().add("useSuffixPatternMatch", true); + wac.registerBeanDefinition("handlerMapping", mappingDef); + }, VariableNamesController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test/foo.json"); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -326,10 +309,7 @@ public class UriTemplateServletAnnotationControllerHandlerMethodTests extends Ab assertThat(response.getContentAsString()).isEqualTo("foo-foo"); } - /* - * See SPR-6978 - */ - @Test + @Test // gh-11643 public void doIt() throws Exception { initServletWithControllers(Spr6978Controller.class); diff --git a/src/docs/asciidoc/web/webmvc.adoc b/src/docs/asciidoc/web/webmvc.adoc index 0a75908144..155fe55074 100644 --- a/src/docs/asciidoc/web/webmvc.adoc +++ b/src/docs/asciidoc/web/webmvc.adoc @@ -1683,11 +1683,11 @@ See <> in the configuration section. [[mvc-ann-requestmapping-suffix-pattern-match]] ==== Suffix Match -By default, Spring MVC performs `.{asterisk}` suffix pattern matching so that a -controller mapped to `/person` is also implicitly mapped to `/person.{asterisk}`. -The file extension is then used to interpret the requested content type to use for -the response (that is, instead of the `Accept` header) -- for example, `/person.pdf`, -`/person.xml`, and others. +Starting in 5.3, by default Spring MVC no longer performs `.{asterisk}` suffix pattern +matching where a controller mapped to `/person` is also implicitly mapped to +`/person.{asterisk}`. As a consequence path extensions are no longer used to interpret +the requested content type for the response -- for example, `/person.pdf`, `/person.xml`, +and so on. Using file extensions in this way was necessary when browsers used to send `Accept` headers that were hard to interpret consistently. At present, that is no longer a necessity and @@ -1698,28 +1698,16 @@ It can cause ambiguity when overlain with the use of URI variables, path paramet URI encoding. Reasoning about URL-based authorization and security (see next section for more details) also become more difficult. -To completely disable the use of file extensions, you must set both of the following: +To completely disable the use of path extensions in versions prior to 5.3, set the following: * `useSuffixPatternMatching(false)`, see <> * `favorPathExtension(false)`, see <> -URL-based content negotiation can still be useful (for example, when typing a URL in a -browser). To enable that, we recommend a query parameter-based strategy to avoid most of -the issues that come with file extensions. Alternatively, if you must use file extensions, consider -restricting them to a list of explicitly registered extensions through the -`mediaTypes` property of <>. - -[NOTE] -==== -Starting in 5.2.4, path extension related options for request mapping in -{api-spring-framework}/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java[RequestMappingHandlerMapping] -and for content negotiation in -{api-spring-framework}/org.springframework.web.accept/ContentNegotiationManagerFactoryBean.java[ContentNegotiationManagerFactoryBean] -are deprecated. See Spring Framework issue -https://github.com/spring-projects/spring-framework/issues/24179[#24179] and related -issues for further plans. -==== - +Having a way to request content types other than through the `"Accept"` header can still +be useful, e.g. when typing a URL in a browser. A safe alternative to path extensions is +to use the query parameter strategy. If you must use file extensions, consider restricting +them to a list of explicitly registered extensions through the `mediaTypes` property of +<>. [[mvc-ann-requestmapping-rfd]] @@ -5851,7 +5839,6 @@ The following example shows how to customize path matching in Java configuration public void configurePathMatch(PathMatchConfigurer configurer) { configurer .setUseTrailingSlashMatch(false) - .setUseRegisteredSuffixPatternMatch(true) .setPathMatcher(antPathMatcher()) .setUrlPathHelper(urlPathHelper()) .addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class)); @@ -5879,7 +5866,6 @@ The following example shows how to customize path matching in Java configuration configurer .setUseSuffixPatternMatch(true) .setUseTrailingSlashMatch(false) - .setUseRegisteredSuffixPatternMatch(true) .setPathMatcher(antPathMatcher()) .setUrlPathHelper(urlPathHelper()) .addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController::class.java)) @@ -5904,7 +5890,6 @@ The following example shows how to achieve the same configuration in XML: