Browse Source

Conditional use of URI vars for data binding in WebFlux

This commit aligns Spring WebFlux with WebMvc in adding URI variables
conditionally if not already in the map. The idea is that data
binding is primarily through form data and query params, with URI
variables, and request headers (to be implemented next) also made
available but without shadowing existing values.

See gh-32676
pull/32966/head
rstoyanchev 2 years ago
parent
commit
23160a43dd
  1. 29
      spring-webflux/src/main/java/org/springframework/web/reactive/BindingContext.java
  2. 25
      spring-webflux/src/test/java/org/springframework/web/reactive/BindingContextTests.java

29
spring-webflux/src/main/java/org/springframework/web/reactive/BindingContext.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@ -18,7 +18,6 @@ package org.springframework.web.reactive; @@ -18,7 +18,6 @@ package org.springframework.web.reactive;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import reactor.core.publisher.Mono;
@ -29,6 +28,7 @@ import org.springframework.core.ReactiveAdapterRegistry; @@ -29,6 +28,7 @@ import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.ui.Model;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.validation.SmartValidator;
@ -209,10 +209,29 @@ public class BindingContext { @@ -209,10 +209,29 @@ public class BindingContext {
@Override
public Mono<Map<String, Object>> getValuesToBind(ServerWebExchange exchange) {
return super.getValuesToBind(exchange).doOnNext(map ->
map.putAll(exchange.<Map<String, String>>getAttributeOrDefault(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, Collections.emptyMap())));
return super.getValuesToBind(exchange).doOnNext(map -> {
Map<String, String> vars = exchange.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
if (!CollectionUtils.isEmpty(vars)) {
vars.forEach((key, value) -> addValueIfNotPresent(map, "URI variable", key, value));
}
});
}
private static void addValueIfNotPresent(
Map<String, Object> map, String label, String name, @Nullable Object value) {
if (value != null) {
if (map.containsKey(name)) {
if (logger.isDebugEnabled()) {
logger.debug(label + " '" + name + "' overridden by request bind value.");
}
}
else {
map.put(name, value);
}
}
}
}

25
spring-webflux/src/test/java/org/springframework/web/reactive/BindingContextTests.java

@ -17,16 +17,20 @@ @@ -17,16 +17,20 @@
package org.springframework.web.reactive;
import java.lang.reflect.Method;
import java.util.Map;
import jakarta.validation.Valid;
import org.junit.jupiter.api.Test;
import org.springframework.beans.testfixture.beans.TestBean;
import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType;
import org.springframework.validation.Errors;
import org.springframework.validation.SmartValidator;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebExchangeDataBinder;
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest;
import org.springframework.web.testfixture.server.MockServerWebExchange;
@ -64,6 +68,27 @@ class BindingContextTests { @@ -64,6 +68,27 @@ class BindingContextTests {
assertThat(binder.getValidatorsToApply()).containsExactly(springValidator);
}
@Test
void uriVariablesAddedConditionally() {
MockServerHttpRequest request = MockServerHttpRequest.post("/path")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body("name=John&age=25");
MockServerWebExchange exchange = MockServerWebExchange.from(request);
exchange.getAttributes().put(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, Map.of("age", "26"));
TestBean testBean = new TestBean();
BindingContext bindingContext = new BindingContext(null);
WebExchangeDataBinder binder = bindingContext.createDataBinder(exchange, testBean, "testBean", null);
binder.bind(exchange).block();
assertThat(testBean.getName()).isEqualTo("John");
assertThat(testBean.getAge()).isEqualTo(25);
}
@SuppressWarnings("unused")
private void handleValidObject(@Valid Foo foo) {

Loading…
Cancel
Save