From 830cc3450fac3332be54865c53f448091f72748b Mon Sep 17 00:00:00 2001 From: Marco Krikke Date: Fri, 21 Nov 2014 17:32:42 +0100 Subject: [PATCH 1/6] Improved DataBinder Javadoc for xxx*yyy pattern matching The default documentation does not mention xxx*yyy pattern matching, which is, however, supported by PatternMatchUtils. Such a pattern can be useful for matching nested properties in all elements of a collection, e.g. property[*].nestedProperty. See gh-699 --- .../validation/DataBinder.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/validation/DataBinder.java b/spring-context/src/main/java/org/springframework/validation/DataBinder.java index 1818b94b48f..9ae262ee070 100644 --- a/spring-context/src/main/java/org/springframework/validation/DataBinder.java +++ b/spring-context/src/main/java/org/springframework/validation/DataBinder.java @@ -418,10 +418,11 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { } /** - * Register fields that should be allowed for binding. Default is all - * fields. Restrict this for example to avoid unwanted modifications - * by malicious users when binding HTTP request parameters. - *

Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching + * Register fields that should be allowed for binding. Default is all fields. + * Restrict this for example to avoid unwanted modifications by malicious users + * when binding HTTP request parameters. + *

Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an arbitrary + * number of pattern parts), as well as direct equality. More sophisticated matching * can be implemented by overriding the {@code isAllowed} method. *

Alternatively, specify a list of disallowed fields. * @param allowedFields array of field names @@ -445,7 +446,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { * Register fields that should not be allowed for binding. Default is none. * Mark fields as disallowed for example to avoid unwanted modifications * by malicious users when binding HTTP request parameters. - *

Supports "xxx*", "*xxx" and "*xxx*" patterns. More sophisticated matching + *

Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an arbitrary + * number of pattern parts), as well as direct equality. More sophisticated matching * can be implemented by overriding the {@code isAllowed} method. *

Alternatively, specify a list of allowed fields. * @param disallowedFields array of field names @@ -772,10 +774,11 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { /** * Return if the given field is allowed for binding. * Invoked for each passed-in property value. - *

The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, - * as well as direct equality, in the specified lists of allowed fields and - * disallowed fields. A field matching a disallowed pattern will not be accepted - * even if it also happens to match a pattern in the allowed list. + *

The default implementation checks for "xxx*", "*xxx", "*xxx*" and "xxx*yyy" + * matches (with an arbitrary number of pattern parts), as well as direct equality, + * in the specified lists of allowed fields and disallowed fields. A field matching + * a disallowed pattern will not be accepted even if it also happens to match a + * pattern in the allowed list. *

Can be overridden in subclasses. * @param field the field to check * @return if the field is allowed From 3f7dec0b0dccdbc84897c4c10e6c35ff152d63d9 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 23 Nov 2021 10:52:05 +0000 Subject: [PATCH 2/6] Polishing contribution Closes gh-699 --- .../validation/DataBinder.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/validation/DataBinder.java b/spring-context/src/main/java/org/springframework/validation/DataBinder.java index 9ae262ee070..612dfc5622a 100644 --- a/spring-context/src/main/java/org/springframework/validation/DataBinder.java +++ b/spring-context/src/main/java/org/springframework/validation/DataBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -419,11 +419,12 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { /** * Register fields that should be allowed for binding. Default is all fields. - * Restrict this for example to avoid unwanted modifications by malicious users - * when binding HTTP request parameters. - *

Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an arbitrary - * number of pattern parts), as well as direct equality. More sophisticated matching - * can be implemented by overriding the {@code isAllowed} method. + * Restrict this for example to avoid unwanted modifications by malicious + * users when binding HTTP request parameters. + *

Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an + * arbitrary number of pattern parts), as well as direct equality. More + * sophisticated matching can be implemented by overriding the + * {@code isAllowed} method. *

Alternatively, specify a list of disallowed fields. * @param allowedFields array of field names * @see #setDisallowedFields @@ -443,12 +444,13 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter { } /** - * Register fields that should not be allowed for binding. Default is none. - * Mark fields as disallowed for example to avoid unwanted modifications - * by malicious users when binding HTTP request parameters. - *

Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an arbitrary - * number of pattern parts), as well as direct equality. More sophisticated matching - * can be implemented by overriding the {@code isAllowed} method. + * Register fields that should not be allowed for binding. Default + * is none. Mark fields as disallowed for example to avoid unwanted + * modifications by malicious users when binding HTTP request parameters. + *

Supports "xxx*", "*xxx", "*xxx*" and "xxx*yyy" matches (with an + * arbitrary number of pattern parts), as well as direct equality. + * More sophisticated matching can be implemented by overriding the + * {@code isAllowed} method. *

Alternatively, specify a list of allowed fields. * @param disallowedFields array of field names * @see #setAllowedFields From 913cc079af07fbb58ea09e73d3fb538714b6a1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?/usr/local/=CE=95=CE=A8=CE=97=CE=95=CE=9B=CE=A9=CE=9D?= Date: Tue, 3 Nov 2015 12:30:34 +0100 Subject: [PATCH 3/6] TagWriter can write empty attribute See gh-910 --- .../web/servlet/tags/form/TagWriter.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java index 87cedb54022..29b62c2b936 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java @@ -97,6 +97,19 @@ public class TagWriter { this.writer.append(" ").append(attributeName).append("=\"") .append(attributeValue).append("\""); } + + /** + * Write an empty HTML attribute with the specified name. + *

Be sure to write all attributes before writing + * any inner text or nested tags. + * @throws IllegalStateException if the opening tag is closed + */ + public void writeAttribute(String attributeName) throws JspException { + if (currentState().isBlockTag()) { + throw new IllegalStateException("Cannot write attributes after opening tag is closed."); + } + this.writer.append(" ").append(attributeName); + } /** * Write an HTML attribute if the supplied value is not {@code null} From 0d7c5626936d07446f4ba3f10df2fcbf8d68c551 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 24 Nov 2021 12:42:04 +0000 Subject: [PATCH 4/6] Polishing contribution Closes gh-910 --- .../web/servlet/tags/form/TagWriter.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java index 29b62c2b936..a8167bcbf34 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/TagWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 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. @@ -97,12 +97,11 @@ public class TagWriter { this.writer.append(" ").append(attributeName).append("=\"") .append(attributeValue).append("\""); } - + /** - * Write an empty HTML attribute with the specified name. - *

Be sure to write all attributes before writing - * any inner text or nested tags. - * @throws IllegalStateException if the opening tag is closed + * Variant of {@link #writeAttribute(String, String)} for writing empty HTML + * attributes without a value such as {@code required}. + * @since 5.3.14 */ public void writeAttribute(String attributeName) throws JspException { if (currentState().isBlockTag()) { From ce0aed216b5cc04fbf52ba7a9007c185593e5625 Mon Sep 17 00:00:00 2001 From: Andreas Grub Date: Wed, 24 Nov 2021 07:47:21 +0100 Subject: [PATCH 5/6] Add getter for RequestMappingInfo.BuilderConfiguration This improves support for programmatic registration of mappings to use the same config as that of the RequestMappingHandlerMapping. See gh-27723 --- .../annotation/RequestMappingHandlerMapping.java | 11 +++++++++++ .../annotation/RequestMappingHandlerMappingTests.java | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 1da7ca1ed8b..6bebaa18df0 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -246,6 +246,17 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi return this.config.getFileExtensions(); } + /** + * Get the configuration to build {@link RequestMappingInfo} + * instances. This is useful for programmatic registration of + * additional mappings following the same configuration as {@link + * #createRequestMappingInfo(RequestMapping, RequestCondition)}. + * + * @return builder configuration to be supplied into {@link RequestMappingInfo.Builder#options}. + */ + public RequestMappingInfo.BuilderConfiguration getRequestMappingInfoBuilderConfiguration() { + return this.config; + } /** * {@inheritDoc} 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 f60e69af0e3..2e6149bc969 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 @@ -79,6 +79,17 @@ public class RequestMappingHandlerMappingTests { return Stream.of(Arguments.of(mapping1, wac1), Arguments.of(mapping2, wac2)); } + @Test + void getRequestMappingInfoBuilderConfiguration() { + RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping(); + handlerMapping.setApplicationContext(new StaticWebApplicationContext()); + + RequestMappingInfo.BuilderConfiguration beforeAfterPropertiesSet = handlerMapping.getRequestMappingInfoBuilderConfiguration(); + assertThat(beforeAfterPropertiesSet).isNotNull(); + handlerMapping.afterPropertiesSet(); + RequestMappingInfo.BuilderConfiguration afterPropertiesSet = handlerMapping.getRequestMappingInfoBuilderConfiguration(); + assertThat(afterPropertiesSet).isNotNull().isNotSameAs(beforeAfterPropertiesSet); + } @Test @SuppressWarnings("deprecation") From 829bed03af2f92b37eb45732e5f2b5113f433bdf Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 24 Nov 2021 13:03:12 +0000 Subject: [PATCH 6/6] Polishing contribution Closes gh-27723 --- .../RequestMappingHandlerMapping.java | 30 ++++++++++++++----- .../RequestMappingHandlerMappingTests.java | 24 ++++++++------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 6bebaa18df0..c3b26415889 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -187,7 +187,6 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi @Override @SuppressWarnings("deprecation") public void afterPropertiesSet() { - this.config = new RequestMappingInfo.BuilderConfiguration(); this.config.setTrailingSlashMatch(useTrailingSlashMatch()); this.config.setContentNegotiationManager(getContentNegotiationManager()); @@ -247,17 +246,19 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi } /** - * Get the configuration to build {@link RequestMappingInfo} - * instances. This is useful for programmatic registration of - * additional mappings following the same configuration as {@link - * #createRequestMappingInfo(RequestMapping, RequestCondition)}. - * - * @return builder configuration to be supplied into {@link RequestMappingInfo.Builder#options}. + * Obtain a {@link RequestMappingInfo.BuilderConfiguration} that can reflects + * the internal configuration of this {@code HandlerMapping} and can be used + * to set {@link RequestMappingInfo.Builder#options(RequestMappingInfo.BuilderConfiguration)}. + *

This is useful for programmatic registration of request mappings via + * {@link #registerHandlerMethod(Object, Method, RequestMappingInfo)}. + * @return the builder configuration that reflects the internal state + * @since 5.3.14 */ - public RequestMappingInfo.BuilderConfiguration getRequestMappingInfoBuilderConfiguration() { + public RequestMappingInfo.BuilderConfiguration getBuilderConfiguration() { return this.config; } + /** * {@inheritDoc} *

Expects a handler to have either a type-level @{@link Controller} @@ -401,6 +402,19 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi updateConsumesCondition(mapping, method); } + /** + * {@inheritDoc} + *

Note: To create the {@link RequestMappingInfo}, + * please use {@link #getBuilderConfiguration()} and set the options on + * {@link RequestMappingInfo.Builder#options(RequestMappingInfo.BuilderConfiguration)} + * to match how this {@code HandlerMapping} is configured. This + * is important for example to ensure use of + * {@link org.springframework.web.util.pattern.PathPattern} or + * {@link org.springframework.util.PathMatcher} based matching. + * @param handler the bean name of the handler or the handler instance + * @param method the method to register + * @param mapping the mapping conditions associated with the handler method + */ @Override protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { super.registerHandlerMethod(handler, method, mapping); 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 2e6149bc969..9ed74e07817 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-2020 the original author or authors. + * Copyright 2002-2021 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. @@ -80,15 +80,15 @@ public class RequestMappingHandlerMappingTests { } @Test - void getRequestMappingInfoBuilderConfiguration() { - RequestMappingHandlerMapping handlerMapping = new RequestMappingHandlerMapping(); - handlerMapping.setApplicationContext(new StaticWebApplicationContext()); + void builderConfiguration() { + RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping(); + mapping.setApplicationContext(new StaticWebApplicationContext()); - RequestMappingInfo.BuilderConfiguration beforeAfterPropertiesSet = handlerMapping.getRequestMappingInfoBuilderConfiguration(); - assertThat(beforeAfterPropertiesSet).isNotNull(); - handlerMapping.afterPropertiesSet(); - RequestMappingInfo.BuilderConfiguration afterPropertiesSet = handlerMapping.getRequestMappingInfoBuilderConfiguration(); - assertThat(afterPropertiesSet).isNotNull().isNotSameAs(beforeAfterPropertiesSet); + RequestMappingInfo.BuilderConfiguration config = mapping.getBuilderConfiguration(); + assertThat(config).isNotNull(); + + mapping.afterPropertiesSet(); + assertThat(mapping.getBuilderConfiguration()).isNotNull().isNotSameAs(config); } @Test @@ -99,7 +99,8 @@ public class RequestMappingHandlerMappingTests { handlerMapping.setApplicationContext(new StaticWebApplicationContext()); Map fileExtensions = Collections.singletonMap("json", MediaType.APPLICATION_JSON); - org.springframework.web.accept.PathExtensionContentNegotiationStrategy strategy = new org.springframework.web.accept.PathExtensionContentNegotiationStrategy(fileExtensions); + org.springframework.web.accept.PathExtensionContentNegotiationStrategy strategy = + new org.springframework.web.accept.PathExtensionContentNegotiationStrategy(fileExtensions); ContentNegotiationManager manager = new ContentNegotiationManager(strategy); handlerMapping.setContentNegotiationManager(manager); @@ -115,7 +116,8 @@ public class RequestMappingHandlerMappingTests { @SuppressWarnings("deprecation") void useRegisteredSuffixPatternMatchInitialization() { Map fileExtensions = Collections.singletonMap("json", MediaType.APPLICATION_JSON); - org.springframework.web.accept.PathExtensionContentNegotiationStrategy strategy = new org.springframework.web.accept.PathExtensionContentNegotiationStrategy(fileExtensions); + org.springframework.web.accept.PathExtensionContentNegotiationStrategy strategy = + new org.springframework.web.accept.PathExtensionContentNegotiationStrategy(fileExtensions); ContentNegotiationManager manager = new ContentNegotiationManager(strategy); final Set extensions = new HashSet<>();