Browse Source

Data binding from pathvars and headers in fn handlers

Closes gh-35800
pull/35899/head
rstoyanchev 3 weeks ago
parent
commit
a9a404266c
  1. 3
      framework-docs/modules/ROOT/pages/web/webflux-functional.adoc
  2. 3
      framework-docs/modules/ROOT/pages/web/webmvc-functional.adoc
  3. 3
      spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerRequest.java
  4. 11
      spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ExtendedWebExchangeDataBinder.java
  5. 10
      spring-webflux/src/test/java/org/springframework/web/reactive/function/server/BindingFunctionIntegrationTests.java
  6. 3
      spring-webmvc/src/main/java/org/springframework/web/servlet/function/DefaultServerRequest.java
  7. 9
      spring-webmvc/src/test/java/org/springframework/web/servlet/function/DefaultServerRequestTests.java

3
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. 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] [tabs]
====== ======

3
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] [tabs]
====== ======

3
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.BodyExtractor;
import org.springframework.web.reactive.function.BodyExtractors; import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.UnsupportedMediaTypeException; 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.ServerWebExchange;
import org.springframework.web.server.ServerWebInputException; import org.springframework.web.server.ServerWebInputException;
import org.springframework.web.server.UnsupportedMediaTypeStatusException; import org.springframework.web.server.UnsupportedMediaTypeStatusException;
@ -244,7 +245,7 @@ class DefaultServerRequest implements ServerRequest {
Assert.notNull(dataBinderCustomizer, "DataBinderCustomizer must not be null"); Assert.notNull(dataBinderCustomizer, "DataBinderCustomizer must not be null");
return Mono.defer(() -> { return Mono.defer(() -> {
WebExchangeDataBinder dataBinder = new WebExchangeDataBinder(null); WebExchangeDataBinder dataBinder = new ExtendedWebExchangeDataBinder(null);
dataBinder.setTargetType(ResolvableType.forClass(bindType)); dataBinder.setTargetType(ResolvableType.forClass(bindType));
dataBinderCustomizer.accept(dataBinder); dataBinderCustomizer.accept(dataBinder);

11
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<String> headerPredicate = name -> !FILTERED_HEADER_NAMES.contains(name.toLowerCase(Locale.ROOT)); private Predicate<String> 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) { public ExtendedWebExchangeDataBinder(@Nullable Object target, String objectName) {
super(target, objectName); super(target, objectName);
} }

10
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; import static org.springframework.web.reactive.function.server.RouterFunctions.route;
/** /**
* Integration tests with data binding in fn handlers.
* @author Arjen Poutsma * @author Arjen Poutsma
*/ */
class BindingFunctionIntegrationTests extends AbstractRouterFunctionIntegrationTests { class BindingFunctionIntegrationTests extends AbstractRouterFunctionIntegrationTests {
@ -65,7 +66,8 @@ class BindingFunctionIntegrationTests extends AbstractRouterFunctionIntegrationT
startServer(httpServer); startServer(httpServer);
Mono<String> result = this.webClient.get() Mono<String> result = this.webClient.get()
.uri("/constructor?foo=FOO&bar=BAR") .uri("/constructor?foo=FOO")
.header("bar", "BAR")
.retrieve() .retrieve()
.bodyToMono(String.class); .bodyToMono(String.class);
@ -80,7 +82,8 @@ class BindingFunctionIntegrationTests extends AbstractRouterFunctionIntegrationT
startServer(httpServer); startServer(httpServer);
Mono<String> result = this.webClient.get() Mono<String> result = this.webClient.get()
.uri("/property?foo=FOO&bar=BAR") .uri("/property?foo=FOO")
.header("bar", "BAR")
.retrieve() .retrieve()
.bodyToMono(String.class); .bodyToMono(String.class);
@ -95,7 +98,8 @@ class BindingFunctionIntegrationTests extends AbstractRouterFunctionIntegrationT
startServer(httpServer); startServer(httpServer);
Mono<String> result = this.webClient.get() Mono<String> result = this.webClient.get()
.uri("/mixed?foo=FOO&bar=BAR") .uri("/mixed?foo=FOO")
.header("bar", "BAR")
.retrieve() .retrieve()
.bodyToMono(String.class); .bodyToMono(String.class);

3
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.bind.WebDataBinder;
import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest; 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.servlet.support.ServletUriComponentsBuilder;
import org.springframework.web.util.ServletRequestPathUtils; import org.springframework.web.util.ServletRequestPathUtils;
import org.springframework.web.util.UriBuilder; import org.springframework.web.util.UriBuilder;
@ -258,7 +259,7 @@ class DefaultServerRequest implements ServerRequest {
Assert.notNull(bindType, "BindType must not be null"); Assert.notNull(bindType, "BindType must not be null");
Assert.notNull(dataBinderCustomizer, "DataBinderCustomizer 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)); dataBinder.setTargetType(ResolvableType.forClass(bindType));
dataBinderCustomizer.accept(dataBinder); dataBinderCustomizer.accept(dataBinder);

9
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; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/** /**
* Tests for {@link DefaultServerRequest}. * Unit tests for {@link DefaultServerRequest}.
*
* @author Arjen Poutsma * @author Arjen Poutsma
*/ */
class DefaultServerRequestTests { class DefaultServerRequestTests {
@ -350,7 +349,7 @@ class DefaultServerRequestTests {
void bindToConstructor() throws BindException { void bindToConstructor() throws BindException {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true); MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.addParameter("foo", "FOO"); servletRequest.addParameter("foo", "FOO");
servletRequest.addParameter("bar", "BAR"); servletRequest.addHeader("bar", "BAR");
DefaultServerRequest request = new DefaultServerRequest(servletRequest, this.messageConverters); DefaultServerRequest request = new DefaultServerRequest(servletRequest, this.messageConverters);
@ -363,7 +362,7 @@ class DefaultServerRequestTests {
void bindToProperties() throws BindException { void bindToProperties() throws BindException {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true); MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.addParameter("foo", "FOO"); servletRequest.addParameter("foo", "FOO");
servletRequest.addParameter("bar", "BAR"); servletRequest.addHeader("bar", "BAR");
DefaultServerRequest request = new DefaultServerRequest(servletRequest, this.messageConverters); DefaultServerRequest request = new DefaultServerRequest(servletRequest, this.messageConverters);
@ -376,7 +375,7 @@ class DefaultServerRequestTests {
void bindToMixed() throws BindException { void bindToMixed() throws BindException {
MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true); MockHttpServletRequest servletRequest = PathPatternsTestUtils.initRequest("GET", "/", true);
servletRequest.addParameter("foo", "FOO"); servletRequest.addParameter("foo", "FOO");
servletRequest.addParameter("bar", "BAR"); servletRequest.addHeader("bar", "BAR");
DefaultServerRequest request = new DefaultServerRequest(servletRequest, this.messageConverters); DefaultServerRequest request = new DefaultServerRequest(servletRequest, this.messageConverters);

Loading…
Cancel
Save