Support declarativeBinding mode in DataBinder
Closes gh-30948
This commit is contained in:
@@ -153,6 +153,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
@Nullable
|
||||
private ExtendedTypeConverter typeConverter;
|
||||
|
||||
private boolean declarativeBinding = false;
|
||||
|
||||
private boolean ignoreUnknownFields = true;
|
||||
|
||||
private boolean ignoreInvalidFields = false;
|
||||
@@ -425,6 +427,28 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
return getInternalBindingResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to bind only fields explicitly intended for binding including:
|
||||
* <ul>
|
||||
* <li>Constructor binding via {@link #construct}.
|
||||
* <li>Property binding with configured
|
||||
* {@link #setAllowedFields(String...) allowedFields}.
|
||||
* </ul>
|
||||
* <p>Default is "false". Turn this on to limit binding to constructor
|
||||
* parameters and allowed fields.
|
||||
* @since 6.1
|
||||
*/
|
||||
public void setDeclarativeBinding(boolean declarativeBinding) {
|
||||
this.declarativeBinding = declarativeBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to bind only fields intended for binding.
|
||||
* @since 6.1
|
||||
*/
|
||||
public boolean isDeclarativeBinding() {
|
||||
return this.declarativeBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to ignore unknown fields, that is, whether to ignore bind
|
||||
@@ -1031,11 +1055,24 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
* @see #doBind(org.springframework.beans.MutablePropertyValues)
|
||||
*/
|
||||
public void bind(PropertyValues pvs) {
|
||||
if (shouldNotBindPropertyValues()) {
|
||||
return;
|
||||
}
|
||||
MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues mutablePropertyValues ?
|
||||
mutablePropertyValues : new MutablePropertyValues(pvs));
|
||||
doBind(mpvs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to not bind parameters to properties. Returns "true" if
|
||||
* {@link #isDeclarativeBinding()} is on, and
|
||||
* {@link #setAllowedFields(String...) allowedFields} are not configured.
|
||||
* @since 6.1
|
||||
*/
|
||||
protected boolean shouldNotBindPropertyValues() {
|
||||
return (isDeclarativeBinding() && ObjectUtils.isEmpty(this.allowedFields));
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual implementation of the binding process, working with the
|
||||
* passed-in MutablePropertyValues instance.
|
||||
|
||||
@@ -681,6 +681,23 @@ class DataBinderTests {
|
||||
assertThat(dataBinder.convertIfNecessary(bean, String.class)).as("Type converter should have been used").isEqualTo("[Fred]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindingInDeclarativeMode() throws BindException {
|
||||
TestBean rod = new TestBean();
|
||||
DataBinder binder = new DataBinder(rod);
|
||||
binder.setDeclarativeBinding(true);
|
||||
|
||||
MutablePropertyValues pvs = new MutablePropertyValues();
|
||||
pvs.add("name", "Rod");
|
||||
pvs.add("age", "32x");
|
||||
|
||||
binder.bind(pvs);
|
||||
binder.close();
|
||||
|
||||
assertThat(rod.getName()).isNull();
|
||||
assertThat(rod.getAge()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bindingWithAllowedFields() throws BindException {
|
||||
TestBean rod = new TestBean();
|
||||
|
||||
@@ -140,6 +140,9 @@ public class ServletRequestDataBinder extends WebDataBinder {
|
||||
* @see #bind(org.springframework.beans.PropertyValues)
|
||||
*/
|
||||
public void bind(ServletRequest request) {
|
||||
if (shouldNotBindPropertyValues()) {
|
||||
return;
|
||||
}
|
||||
MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
|
||||
MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
|
||||
if (multipartRequest != null) {
|
||||
|
||||
@@ -44,6 +44,9 @@ public class ConfigurableWebBindingInitializer implements WebBindingInitializer
|
||||
|
||||
private boolean directFieldAccess = false;
|
||||
|
||||
@Nullable
|
||||
private Boolean declarativeBinding;
|
||||
|
||||
@Nullable
|
||||
private MessageCodesResolver messageCodesResolver;
|
||||
|
||||
@@ -99,6 +102,23 @@ public class ConfigurableWebBindingInitializer implements WebBindingInitializer
|
||||
return this.directFieldAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to bind only fields intended for binding as described in
|
||||
* {@link org.springframework.validation.DataBinder#setDeclarativeBinding}.
|
||||
* @since 6.1
|
||||
*/
|
||||
public void setDeclarativeBinding(boolean declarativeBinding) {
|
||||
this.declarativeBinding = declarativeBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to bind only fields intended for binding.
|
||||
* @since 6.1
|
||||
*/
|
||||
public boolean isDeclarativeBinding() {
|
||||
return (this.declarativeBinding != null ? this.declarativeBinding : false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the strategy to use for resolving errors into message codes.
|
||||
* Applies the given strategy to all data binders used by this controller.
|
||||
@@ -197,6 +217,9 @@ public class ConfigurableWebBindingInitializer implements WebBindingInitializer
|
||||
if (this.directFieldAccess) {
|
||||
binder.initDirectFieldAccess();
|
||||
}
|
||||
if (this.declarativeBinding != null) {
|
||||
binder.setDeclarativeBinding(this.declarativeBinding);
|
||||
}
|
||||
if (this.messageCodesResolver != null) {
|
||||
binder.setMessageCodesResolver(this.messageCodesResolver);
|
||||
}
|
||||
|
||||
@@ -93,6 +93,9 @@ public class WebExchangeDataBinder extends WebDataBinder {
|
||||
* @return a {@code Mono<Void>} that completes when binding is complete
|
||||
*/
|
||||
public Mono<Void> bind(ServerWebExchange exchange) {
|
||||
if (shouldNotBindPropertyValues()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
return getValuesToBind(exchange)
|
||||
.doOnNext(map -> doBind(new MutablePropertyValues(map)))
|
||||
.then();
|
||||
|
||||
@@ -138,6 +138,9 @@ public class WebRequestDataBinder extends WebDataBinder {
|
||||
* @see #bind(org.springframework.beans.PropertyValues)
|
||||
*/
|
||||
public void bind(WebRequest request) {
|
||||
if (shouldNotBindPropertyValues()) {
|
||||
return;
|
||||
}
|
||||
MutablePropertyValues mpvs = new MutablePropertyValues(request.getParameterMap());
|
||||
if (request instanceof NativeWebRequest nativeRequest) {
|
||||
MultipartRequest multipartRequest = nativeRequest.getNativeRequest(MultipartRequest.class);
|
||||
|
||||
@@ -2056,6 +2056,26 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
|
||||
assertThat(response.getContentAsString()).isEqualTo("value1-true-3");
|
||||
}
|
||||
|
||||
@PathPatternsParameterizedTest
|
||||
void dataClassBindingWithAdditionalSetterInDeclarativeBindingMode(boolean usePathPatterns) throws Exception {
|
||||
initDispatcherServlet(DataClassController.class, usePathPatterns, wac -> {
|
||||
ConfigurableWebBindingInitializer initializer = new ConfigurableWebBindingInitializer();
|
||||
initializer.setDeclarativeBinding(true);
|
||||
|
||||
RootBeanDefinition mappingDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
|
||||
mappingDef.getPropertyValues().add("webBindingInitializer", initializer);
|
||||
wac.registerBeanDefinition("handlerAdapter", mappingDef);
|
||||
});
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bind");
|
||||
request.addParameter("param1", "value1");
|
||||
request.addParameter("param2", "true");
|
||||
request.addParameter("param3", "3");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
getServlet().service(request, response);
|
||||
assertThat(response.getContentAsString()).isEqualTo("value1-true-0");
|
||||
}
|
||||
|
||||
@PathPatternsParameterizedTest
|
||||
void dataClassBindingWithResult(boolean usePathPatterns) throws Exception {
|
||||
initDispatcherServlet(ValidatedDataClassController.class, usePathPatterns);
|
||||
|
||||
Reference in New Issue
Block a user