Browse Source

Update documentation for data binding improvements

Closes gh-30952
pull/30993/head
rstoyanchev 2 years ago
parent
commit
8513ec7440
  1. 55
      framework-docs/modules/ROOT/pages/core/validation/beans-beans.adoc
  2. 44
      framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc
  3. 2
      framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-initbinder.adoc
  4. 44
      framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc
  5. 35
      framework-docs/modules/ROOT/partials/web/web-data-binding-model-design.adoc

55
framework-docs/modules/ROOT/pages/core/validation/beans-beans.adoc

@ -1,5 +1,50 @@ @@ -1,5 +1,50 @@
[[beans-binding]]
= Data Binding
Data binding is useful for binding user input to a target object where user input is a map
with property paths as keys, following xref:beans-beans-conventions[JavaBeans conventions].
`DataBinder` is the main class that supports this, and it provides two ways to bind user
input:
- xref:beans-constructor-binding[Constructor binding] - bind user input to a public data
constructor, looking up constructor argument values in the user input.
- xref:beans-beans[Property binding] - bind user input to setters, matching keys from the
the user input to properties of the target object structure.
You can apply both constructor and property binding or only one.
[[beans-constructor-binding]]
== Constructor Binding
To use constructor binding:
1. Create a `DataBinder` with `null` as the target object.
2. Set `targetType` to the target class.
3. Call `construct`.
The target class should have a single public constructor or a single non-public constructor
with arguments. If there are multiple constructors, then a default constructor if present
is used.
By default, constructor parameter names are used to look up argument values, but you can
configure a `NameResolver`. Spring MVC and WebFlux both rely to allow customizing the name
of the value to bind through an `@BindParam` annotation on constructor parameters.
xref:beans-beans-conventions[Type conversion] is applied as needed to convert user input.
If the constructor parameter is an object, it is constructed recursively in the same
manner, but through a nested property path. That means constructor binding creates both
the target object and any objects it contains.
Binding and conversion errors are reflected in the `BindingResult` of the `DataBinder`.
If the target is created successfully, then `target` is set to the created instance
after the call to `construct`.
[[beans-beans]]
= Bean Manipulation and the `BeanWrapper`
== Property Binding with `BeanWrapper`
The `org.springframework.beans` package adheres to the JavaBeans standard.
A JavaBean is a class with a default no-argument constructor and that follows
@ -26,7 +71,7 @@ perform actions on that bean, such as setting and retrieving properties. @@ -26,7 +71,7 @@ perform actions on that bean, such as setting and retrieving properties.
[[beans-beans-conventions]]
== Setting and Getting Basic and Nested Properties
=== Setting and Getting Basic and Nested Properties
Setting and getting properties is done through the `setPropertyValue` and
`getPropertyValue` overloaded method variants of `BeanWrapper`. See their Javadoc for
@ -192,7 +237,7 @@ Kotlin:: @@ -192,7 +237,7 @@ Kotlin::
[[beans-beans-conversion]]
== Built-in `PropertyEditor` Implementations
== ``PropertyEditor``'s
Spring uses the concept of a `PropertyEditor` to effect the conversion between an
`Object` and a `String`. It can be handy
@ -378,7 +423,7 @@ Kotlin:: @@ -378,7 +423,7 @@ Kotlin::
[[beans-beans-conversion-customeditor-registration]]
=== Registering Additional Custom `PropertyEditor` Implementations
=== Custom ``PropertyEditor``'s
When setting bean properties as string values, a Spring IoC container ultimately uses
standard JavaBeans `PropertyEditor` implementations to convert these strings to the complex type of the
@ -521,7 +566,7 @@ Finally, the following example shows how to use `CustomEditorConfigurer` to regi @@ -521,7 +566,7 @@ Finally, the following example shows how to use `CustomEditorConfigurer` to regi
----
[[beans-beans-conversion-customeditor-registration-per]]
==== Using `PropertyEditorRegistrar`
=== `PropertyEditorRegistrar`
Another mechanism for registering property editors with the Spring container is to
create and use a `PropertyEditorRegistrar`. This interface is particularly useful when

44
framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc

@ -38,12 +38,44 @@ The `Pet` instance may be: @@ -38,12 +38,44 @@ The `Pet` instance may be:
request parameters. Argument names are determined through runtime-retained parameter
names in the bytecode.
Once a model attribute instance is available, `WebDataBinder` binds request parameters to
properties of the target `Object` with type conversion where necessary.
For more on data binding and validation, see
xref:web/webmvc/mvc-config/validation.adoc[Validation].
For more on customizing data binding, see
xref:web/webmvc/mvc-controller/ann-initbinder.adoc[DataBinder].
By default, both constructor and property
xref:core/validation/beans-beans.adoc#beans-binding[data binding] are applied. However,
model object design requires careful consideration, and for security reasons it is
recommended either to use an object tailored specifically for web binding, or to apply
constructor binding only. If property binding must still be used, then _allowedFields_
patterns should be set to limit which properties can be set. For further details on this
and example configuration, see
xref:web/webflux/controller/ann-initbinder.adoc#webflux-ann-initbinder-model-design[model design].
When using constructor binding, you can customize request parameter names through an
`@BindParam` annotation. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class Account {
private final String firstName;
public Account(@BindParam("first-name") String firstName) {
this.firstName = firstName;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class Account(@BindParam("first-name") val firstName: String)
----
======
NOTE: The `@BindParam` may also be placed on the fields that correspond to constructor
parameters. While `@BindParam` is supported out of the box, you can also use a
different annotation by setting a `DataBinder.NameResolver` on `DataBinder`
WebFlux, unlike Spring MVC, supports reactive types in the model, e.g. `Mono<Account>`.
You can declare a `@ModelAttribute` argument with or without a reactive type wrapper, and

2
framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-initbinder.adoc

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
[[mvc-ann-initbinder]]
= `DataBinder`
= `@InitBinder`
[.small]#xref:web/webflux/controller/ann-initbinder.adoc[See equivalent in the Reactive stack]#

44
framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc

@ -73,12 +73,44 @@ Kotlin:: @@ -73,12 +73,44 @@ Kotlin::
----
======
Once a model attribute instance is available, `WebDataBinder` binds request parameters to
properties of the target `Object` with type conversion where necessary.
For more on data binding and validation, see
xref:web/webmvc/mvc-config/validation.adoc[Validation].
For more on customizing data binding, see
xref:web/webmvc/mvc-controller/ann-initbinder.adoc[DataBinder].
By default, both constructor and property
xref:core/validation/beans-beans.adoc#beans-binding[data binding] are applied. However,
model object design requires careful consideration, and for security reasons it is
recommended either to use an object tailored specifically for web binding, or to apply
constructor binding only. If property binding must still be used, then _allowedFields_
patterns should be set to limit which properties can be set. For further details on this
and example configuration, see
xref:web/webmvc/mvc-controller/ann-initbinder.adoc#mvc-ann-initbinder-model-design[model design].
When using constructor binding, you can customize request parameter names through an
`@BindParam` annotation. For example:
[tabs]
======
Java::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
class Account {
private final String firstName;
public Account(@BindParam("first-name") String firstName) {
this.firstName = firstName;
}
}
----
Kotlin::
+
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
----
class Account(@BindParam("first-name") val firstName: String)
----
======
NOTE: The `@BindParam` may also be placed on the fields that correspond to constructor
parameters. While `@BindParam` is supported out of the box, you can also use a
different annotation by setting a `DataBinder.NameResolver` on `DataBinder`
In some cases, you may want access to a model attribute without data binding. For such
cases, you can inject the `Model` into the controller and access it directly or,

35
framework-docs/modules/ROOT/partials/web/web-data-binding-model-design.adoc

@ -38,9 +38,16 @@ the properties required for the input: @@ -38,9 +38,16 @@ the properties required for the input:
}
----
If a dedicated model object is not feasible, we strongy recommend registering
`allowedFields` patterns (case sensitive) on `WebDataBinder` in order to prevent other
properties from being set. For example:
Another good practice is to apply
xref:core/validation/beans-beans.adoc#beans-constructor-binding[constructor binding],
which uses only the request parameters it needs for constructor arguments, and any other
input is ignored. This is in contrast to property binding which by default binds every
request parameter for which there is a matching property.
If neither a dedicated model object nor constructor binding is sufficient, and you must
use property binding, we strongy recommend registering `allowedFields` patterns (case
sensitive) on `WebDataBinder` in order to prevent unexpected properties from being set.
For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@ -60,3 +67,25 @@ properties from being set. For example: @@ -60,3 +67,25 @@ properties from being set. For example:
You can also register `disallowedFields` patterns (case insensitive). However,
"allowed" configuration is preferred over "disallowed" as it is more explicit and less
prone to mistakes.
By default, constructor and property binding are both used. If you want to use
constructor binding only, you can set the `declarativeBinding` flag on `WebDataBinder`
through an `@InitBinder` method either locally within a controller or globally through an
`@ControllerAdvice`. Turning this flag on ensures that only constructor binding is used
and that property binding is not used unless `allowedFields` patterns are configured.
For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
@Controller
public class MyController {
@InitBinder
void initBinder(WebDataBinder binder) {
binder.setDeclarativeBinding(true);
}
// @RequestMapping methods, etc.
}
----

Loading…
Cancel
Save