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..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. @@ -418,11 +418,13 @@ 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 - * can be implemented by overriding the {@code isAllowed} method. + * 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 * @see #setDisallowedFields @@ -442,11 +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" and "*xxx*" patterns. 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 @@ -772,10 +776,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 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 a5061118a97..bfb424d433f 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()); @@ -246,6 +245,19 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi return this.config.getFileExtensions(); } + /** + * 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 getBuilderConfiguration() { + return this.config; + } + /** * {@inheritDoc} @@ -390,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/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 5c147ea258d..ff4175ab912 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. @@ -98,6 +98,18 @@ public class TagWriter { .append(attributeValue).append("\""); } + /** + * 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()) { + 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} * or zero length. 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..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. @@ -79,6 +79,17 @@ public class RequestMappingHandlerMappingTests { return Stream.of(Arguments.of(mapping1, wac1), Arguments.of(mapping2, wac2)); } + @Test + void builderConfiguration() { + RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping(); + mapping.setApplicationContext(new StaticWebApplicationContext()); + + RequestMappingInfo.BuilderConfiguration config = mapping.getBuilderConfiguration(); + assertThat(config).isNotNull(); + + mapping.afterPropertiesSet(); + assertThat(mapping.getBuilderConfiguration()).isNotNull().isNotSameAs(config); + } @Test @SuppressWarnings("deprecation") @@ -88,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); @@ -104,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<>();