diff --git a/framework-docs/modules/ROOT/pages/web/webflux-functional.adoc b/framework-docs/modules/ROOT/pages/web/webflux-functional.adoc index 6388b3a1ec7..a86167882c6 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux-functional.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux-functional.adoc @@ -296,7 +296,8 @@ allPartsEvents.windowUntil(PartEvent::isLast) NOTE: The body contents of the `PartEvent` objects must be completely consumed, relayed, or released to avoid memory leaks. -The following shows how to bind request parameters, including an optional `DataBinder` customization: +The following shows how to bind request parameters, URI variables, or headers via `DataBinder`, +and also shows how to customize the `DataBinder`: [tabs] ====== diff --git a/framework-docs/modules/ROOT/pages/web/webmvc-functional.adoc b/framework-docs/modules/ROOT/pages/web/webmvc-functional.adoc index 5901b99fb94..1b3097b691a 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc-functional.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc-functional.adoc @@ -184,7 +184,8 @@ val map = request.params() ---- ====== -The following shows how to bind request parameters, including an optional `DataBinder` customization: +The following shows how to bind request parameters, URI variables, or headers via `DataBinder`, +and also shows how to customize the `DataBinder`: [tabs] ====== diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java index 1a894f5c30d..9a6aef18f82 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java @@ -61,6 +61,7 @@ import org.springframework.web.reactive.accept.ApiVersionStrategy; import org.springframework.web.reactive.function.BodyExtractor; import org.springframework.web.reactive.function.BodyExtractors; import org.springframework.web.reactive.function.UnsupportedMediaTypeException; +import org.springframework.web.reactive.result.method.annotation.ExtendedWebExchangeDataBinder; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebInputException; import org.springframework.web.server.UnsupportedMediaTypeStatusException; @@ -244,7 +245,7 @@ class DefaultServerRequest implements ServerRequest { Assert.notNull(dataBinderCustomizer, "DataBinderCustomizer must not be null"); return Mono.defer(() -> { - WebExchangeDataBinder dataBinder = new WebExchangeDataBinder(null); + WebExchangeDataBinder dataBinder = new ExtendedWebExchangeDataBinder(null); dataBinder.setTargetType(ResolvableType.forClass(bindType)); dataBinderCustomizer.accept(dataBinder); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ExtendedWebExchangeDataBinder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ExtendedWebExchangeDataBinder.java index e72e2f9a09d..0c055d2a0ec 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ExtendedWebExchangeDataBinder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ExtendedWebExchangeDataBinder.java @@ -51,6 +51,17 @@ public class ExtendedWebExchangeDataBinder extends WebExchangeDataBinder { private Predicate headerPredicate = name -> !FILTERED_HEADER_NAMES.contains(name.toLowerCase(Locale.ROOT)); + /** + * Create a new instance, with default object name. + * @param target the target object to bind onto (or {@code null} if the + * binder is just used to convert a plain parameter value) + * @since 7.0.2 + * @see #DEFAULT_OBJECT_NAME + */ + public ExtendedWebExchangeDataBinder(@Nullable Object target) { + super(target); + } + public ExtendedWebExchangeDataBinder(@Nullable Object target, String objectName) { super(target, objectName); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/BindingFunctionIntegrationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/BindingFunctionIntegrationTests.java index eecbce21fb6..2b28853c1d6 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/BindingFunctionIntegrationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/BindingFunctionIntegrationTests.java @@ -30,6 +30,7 @@ import org.springframework.web.testfixture.http.server.reactive.bootstrap.HttpSe import static org.springframework.web.reactive.function.server.RouterFunctions.route; /** + * Integration tests with data binding in fn handlers. * @author Arjen Poutsma */ class BindingFunctionIntegrationTests extends AbstractRouterFunctionIntegrationTests { @@ -65,7 +66,8 @@ class BindingFunctionIntegrationTests extends AbstractRouterFunctionIntegrationT startServer(httpServer); Mono result = this.webClient.get() - .uri("/constructor?foo=FOO&bar=BAR") + .uri("/constructor?foo=FOO") + .header("bar", "BAR") .retrieve() .bodyToMono(String.class); @@ -80,7 +82,8 @@ class BindingFunctionIntegrationTests extends AbstractRouterFunctionIntegrationT startServer(httpServer); Mono result = this.webClient.get() - .uri("/property?foo=FOO&bar=BAR") + .uri("/property?foo=FOO") + .header("bar", "BAR") .retrieve() .bodyToMono(String.class); @@ -95,7 +98,8 @@ class BindingFunctionIntegrationTests extends AbstractRouterFunctionIntegrationT startServer(httpServer); Mono result = this.webClient.get() - .uri("/mixed?foo=FOO&bar=BAR") + .uri("/mixed?foo=FOO") + .header("bar", "BAR") .retrieve() .bodyToMono(String.class); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java index b20f3b6bb2e..2cd368bed69 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java @@ -76,6 +76,7 @@ import org.springframework.web.bind.ServletRequestDataBinder; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.springframework.web.util.ServletRequestPathUtils; import org.springframework.web.util.UriBuilder; @@ -258,7 +259,7 @@ class DefaultServerRequest implements ServerRequest { Assert.notNull(bindType, "BindType must not be null"); Assert.notNull(dataBinderCustomizer, "DataBinderCustomizer must not be null"); - ServletRequestDataBinder dataBinder = new ServletRequestDataBinder(null); + ServletRequestDataBinder dataBinder = new ExtendedServletRequestDataBinder(null); dataBinder.setTargetType(ResolvableType.forClass(bindType)); dataBinderCustomizer.accept(dataBinder); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/function/DefaultServerRequestTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/function/DefaultServerRequestTests.java index 6de44eeef99..ce17f09353c 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/function/DefaultServerRequestTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/function/DefaultServerRequestTests.java @@ -64,8 +64,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; /** - * Tests for {@link DefaultServerRequest}. - * + * Unit tests for {@link DefaultServerRequest}. * @author Arjen Poutsma */ class DefaultServerRequestTests { @@ -350,7 +349,7 @@ class DefaultServerRequestTests { void bindToConstructor() throws BindException { MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true); servletRequest.addParameter("foo", "FOO"); - servletRequest.addParameter("bar", "BAR"); + servletRequest.addHeader("bar", "BAR"); DefaultServerRequest request = new DefaultServerRequest(servletRequest, this.messageConverters); @@ -363,7 +362,7 @@ class DefaultServerRequestTests { void bindToProperties() throws BindException { MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true); servletRequest.addParameter("foo", "FOO"); - servletRequest.addParameter("bar", "BAR"); + servletRequest.addHeader("bar", "BAR"); DefaultServerRequest request = new DefaultServerRequest(servletRequest, this.messageConverters); @@ -376,7 +375,7 @@ class DefaultServerRequestTests { void bindToMixed() throws BindException { MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true); servletRequest.addParameter("foo", "FOO"); - servletRequest.addParameter("bar", "BAR"); + servletRequest.addHeader("bar", "BAR"); DefaultServerRequest request = new DefaultServerRequest(servletRequest, this.messageConverters);