Polishing
See gh-30952
This commit is contained in:
committed by
rstoyanchev
parent
bd23798323
commit
667eb42a63
@@ -3,23 +3,21 @@
|
||||
|
||||
[.small]#xref:web/webmvc/mvc-controller/ann-initbinder.adoc[See equivalent in the Servlet stack]#
|
||||
|
||||
`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods, to
|
||||
initialize instances of `WebDataBinder`. Those, in turn, are used to:
|
||||
`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods to
|
||||
initialize `WebDataBinder` instances that in turn can:
|
||||
|
||||
* Bind request parameters (that is, form data or query) to a model object.
|
||||
* Convert `String`-based request values (such as request parameters, path variables,
|
||||
headers, cookies, and others) to the target type of controller method arguments.
|
||||
* Format model object values as `String` values when rendering HTML forms.
|
||||
* Bind request parameters to a model object.
|
||||
* Convert request values from string to object property types.
|
||||
* Format model object properties as strings when rendering HTML forms.
|
||||
|
||||
`@InitBinder` methods can register controller-specific `java.beans.PropertyEditor` or
|
||||
Spring `Converter` and `Formatter` components. In addition, you can use the
|
||||
xref:web/webflux/config.adoc#webflux-config-conversion[WebFlux Java configuration] to register `Converter` and
|
||||
`Formatter` types in a globally shared `FormattingConversionService`.
|
||||
In an `@Controller`, `DataBinder` customizations apply locally within the controller,
|
||||
or even to a specific model attribute referenced by name through the annotation.
|
||||
In an `@ControllerAdvice` customizations can apply to all or a subset of controllers.
|
||||
|
||||
`@InitBinder` methods support many of the same arguments that `@RequestMapping` methods
|
||||
do, except for `@ModelAttribute` (command object) arguments. Typically, they are declared
|
||||
with a `WebDataBinder` argument, for registrations, and a `void` return value.
|
||||
The following example uses the `@InitBinder` annotation:
|
||||
You can register `PropertyEditor`, `Converter`, and `Formatter` components in the
|
||||
`DataBinder` for type conversion. Alternatively, you can use the
|
||||
xref:web/webflux/config.adoc#webflux-config-conversion[WebFlux config] to register
|
||||
`Converter` and `Formatter` components in a globally shared `FormattingConversionService`.
|
||||
|
||||
--
|
||||
[tabs]
|
||||
@@ -112,4 +110,5 @@ Kotlin::
|
||||
== Model Design
|
||||
[.small]#xref:web/webmvc/mvc-controller/ann-initbinder.adoc#mvc-ann-initbinder-model-design[See equivalent in the Servlet stack]#
|
||||
|
||||
include::partial$web/web-data-binding-model-design.adoc[]
|
||||
|
||||
|
||||
@@ -3,11 +3,8 @@
|
||||
|
||||
[.small]#xref:web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc[See equivalent in the Servlet stack]#
|
||||
|
||||
You can use the `@ModelAttribute` annotation on a method argument to access an attribute from the
|
||||
model or have it instantiated if not present. The model attribute is also overlaid with
|
||||
the values of query parameters and form fields whose names match to field names. This is
|
||||
referred to as data binding, and it saves you from having to deal with parsing and
|
||||
converting individual query parameters and form fields. The following example binds an instance of `Pet`:
|
||||
The `@ModelAttribute` method parameter annotation binds request parameters onto a model
|
||||
object. For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -18,7 +15,7 @@ Java::
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
public String processSubmit(@ModelAttribute Pet pet) { } // <1>
|
||||
----
|
||||
<1> Bind an instance of `Pet`.
|
||||
<1> Bind to an instance of `Pet`.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
@@ -27,28 +24,34 @@ Kotlin::
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
fun processSubmit(@ModelAttribute pet: Pet): String { } // <1>
|
||||
----
|
||||
<1> Bind an instance of `Pet`.
|
||||
<1> Bind to an instance of `Pet`.
|
||||
======
|
||||
|
||||
The `Pet` instance in the preceding example is resolved as follows:
|
||||
The `Pet` instance may be:
|
||||
|
||||
* From the model if already added through xref:web/webflux/controller/ann-modelattrib-methods.adoc[`Model`].
|
||||
* From the HTTP session through xref:web/webflux/controller/ann-methods/sessionattributes.adoc[`@SessionAttributes`].
|
||||
* From the invocation of a default constructor.
|
||||
* From the invocation of a "`primary constructor`" with arguments that match query
|
||||
parameters or form fields. Argument names are determined through JavaBeans
|
||||
`@ConstructorProperties` or through runtime-retained parameter names in the bytecode.
|
||||
* Accessed from the model where it could have been added by a
|
||||
xref:web/webflux/controller/ann-modelattrib-methods.adoc[`Model`].
|
||||
* Accessed from the HTTP session if the model attribute was listed in
|
||||
the class-level xref:web/webflux/controller/ann-methods/sessionattributes.adoc[`@SessionAttributes`].
|
||||
* Instantiated through a default constructor.
|
||||
* Instantiated through a "`primary constructor`" with arguments that match to Servlet
|
||||
request parameters. Argument names are determined through runtime-retained parameter
|
||||
names in the bytecode.
|
||||
|
||||
After the model attribute instance is obtained, data binding is applied. The
|
||||
`WebExchangeDataBinder` class matches names of query parameters and form fields to field
|
||||
names on the target `Object`. Matching fields are populated after type conversion is applied
|
||||
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/webflux/controller/ann-initbinder.adoc[`DataBinder`].
|
||||
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].
|
||||
|
||||
Data binding can result in errors. By default, a `WebExchangeBindException` is raised, but,
|
||||
to check for such errors in the controller method, you can add a `BindingResult` argument
|
||||
immediately next to the `@ModelAttribute`, as the following example shows:
|
||||
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
|
||||
it will be resolved accordingly to the actual value.
|
||||
|
||||
If data binding results in errors, by default a `WebExchangeBindException` is raised,
|
||||
but you can also add a `BindingResult` argument immediately next to the `@ModelAttribute`
|
||||
in order to handle such errors in the controller method. For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -81,49 +84,9 @@ Kotlin::
|
||||
<1> Adding a `BindingResult`.
|
||||
======
|
||||
|
||||
You can automatically apply validation after data binding by adding the
|
||||
`jakarta.validation.Valid` annotation or Spring's `@Validated` annotation (see also
|
||||
xref:core/validation/beanvalidation.adoc[Bean Validation] and
|
||||
xref:web/webmvc/mvc-config/validation.adoc[Spring validation]). The following example uses the `@Valid` annotation:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { // <1>
|
||||
if (result.hasErrors()) {
|
||||
return "petForm";
|
||||
}
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> Using `@Valid` on a model attribute argument.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String { // <1>
|
||||
if (result.hasErrors()) {
|
||||
return "petForm"
|
||||
}
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> Using `@Valid` on a model attribute argument.
|
||||
======
|
||||
|
||||
Spring WebFlux, unlike Spring MVC, supports reactive types in the model -- for example,
|
||||
`Mono<Account>` or `io.reactivex.Single<Account>`. You can declare a `@ModelAttribute` argument
|
||||
with or without a reactive type wrapper, and it will be resolved accordingly,
|
||||
to the actual value if necessary. However, note that, to use a `BindingResult`
|
||||
argument, you must declare the `@ModelAttribute` argument before it without a reactive
|
||||
type wrapper, as shown earlier. Alternatively, you can handle any errors through the
|
||||
reactive type, as the following example shows:
|
||||
To use a `BindingResult` argument, you must declare the `@ModelAttribute` argument before
|
||||
it without a reactive type wrapper. If you want to use the reactive, you can handle errors
|
||||
directly through it. For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -160,6 +123,42 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
You can automatically apply validation after data binding by adding the
|
||||
`jakarta.validation.Valid` annotation or Spring's `@Validated` annotation (see
|
||||
xref:core/validation/beanvalidation.adoc[Bean Validation] and
|
||||
xref:web/webmvc/mvc-config/validation.adoc[Spring validation]). For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { // <1>
|
||||
if (result.hasErrors()) {
|
||||
return "petForm";
|
||||
}
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> Using `@Valid` on a model attribute argument.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
fun processSubmit(@Valid @ModelAttribute("pet") pet: Pet, result: BindingResult): String { // <1>
|
||||
if (result.hasErrors()) {
|
||||
return "petForm"
|
||||
}
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> Using `@Valid` on a model attribute argument.
|
||||
======
|
||||
|
||||
If method validation applies because other parameters have `@Constraint` annotations,
|
||||
then `HandlerMethodValidationException` would be raised instead. See the section on
|
||||
controller method xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].
|
||||
|
||||
@@ -3,23 +3,25 @@
|
||||
|
||||
[.small]#xref:web/webflux/controller/ann-initbinder.adoc[See equivalent in the Reactive stack]#
|
||||
|
||||
`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods that
|
||||
initialize instances of `WebDataBinder`, and those, in turn, can:
|
||||
`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods to
|
||||
initialize `WebDataBinder` instances that in turn can:
|
||||
|
||||
* Bind request parameters (that is, form or query data) to a model object.
|
||||
* Convert String-based request values (such as request parameters, path variables,
|
||||
headers, cookies, and others) to the target type of controller method arguments.
|
||||
* Format model object values as `String` values when rendering HTML forms.
|
||||
* Bind request parameters to a model object.
|
||||
* Convert request values from string to object property types.
|
||||
* Format model object properties as strings when rendering HTML forms.
|
||||
|
||||
`@InitBinder` methods can register controller-specific `java.beans.PropertyEditor` or
|
||||
Spring `Converter` and `Formatter` components. In addition, you can use the
|
||||
xref:web/webmvc/mvc-config/conversion.adoc[MVC config] to register `Converter` and `Formatter`
|
||||
types in a globally shared `FormattingConversionService`.
|
||||
In an `@Controller`, `DataBinder` customizations apply locally within the controller,
|
||||
or even to a specific model attribute referenced by name through the annotation.
|
||||
In an `@ControllerAdvice` customizations can apply to all or a subset of controllers.
|
||||
|
||||
`@InitBinder` methods support many of the same arguments that `@RequestMapping` methods
|
||||
do, except for `@ModelAttribute` (command object) arguments. Typically, they are declared
|
||||
with a `WebDataBinder` argument (for registrations) and a `void` return value.
|
||||
The following listing shows an example:
|
||||
You can register `PropertyEditor`, `Converter`, and `Formatter` components in the
|
||||
`DataBinder` for type conversion. Alternatively, you can use the
|
||||
xref:web/webmvc/mvc-config/conversion.adoc[MVC config] to register `Converter` and
|
||||
`Formatter` components in a globally shared `FormattingConversionService`.
|
||||
|
||||
`@InitBinder` methods can have many of the same arguments that `@RequestMapping` methods
|
||||
have, with the notable exception of `@ModelAttribute`. Typically, such methods have a
|
||||
`WebDataBinder` argument (for registrations) and a `void` return value, for example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
|
||||
@@ -3,11 +3,8 @@
|
||||
|
||||
[.small]#xref:web/webflux/controller/ann-methods/modelattrib-method-args.adoc[See equivalent in the Reactive stack]#
|
||||
|
||||
You can use the `@ModelAttribute` annotation on a method argument to access an attribute from
|
||||
the model or have it be instantiated if not present. The model attribute is also overlain with
|
||||
values from HTTP Servlet request parameters whose names match to field names. This is referred
|
||||
to as data binding, and it saves you from having to deal with parsing and converting individual
|
||||
query parameters and form fields. The following example shows how to do so:
|
||||
The `@ModelAttribute` method parameter annotation binds request parameters onto a model
|
||||
object. For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -20,7 +17,7 @@ Java::
|
||||
// method logic...
|
||||
}
|
||||
----
|
||||
<1> Bind an instance of `Pet`.
|
||||
<1> Bind to an instance of `Pet`.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
@@ -31,30 +28,27 @@ fun processSubmit(@ModelAttribute pet: Pet): String { // <1>
|
||||
// method logic...
|
||||
}
|
||||
----
|
||||
<1> Bind an instance of `Pet`.
|
||||
<1> Bind to an instance of `Pet`.
|
||||
======
|
||||
|
||||
The `Pet` instance above is sourced in one of the following ways:
|
||||
The `Pet` instance may be:
|
||||
|
||||
* Retrieved from the model where it may have been added by a
|
||||
* Accessed from the model where it could have been added by a
|
||||
xref:web/webmvc/mvc-controller/ann-modelattrib-methods.adoc[@ModelAttribute method].
|
||||
* Retrieved from the HTTP session if the model attribute was listed in
|
||||
* Accessed from the HTTP session if the model attribute was listed in
|
||||
the class-level xref:web/webmvc/mvc-controller/ann-methods/sessionattributes.adoc[`@SessionAttributes`] annotation.
|
||||
* Obtained through a `Converter` where the model attribute name matches the name of a
|
||||
request value such as a path variable or a request parameter (see next example).
|
||||
* Instantiated using its default constructor.
|
||||
* Obtained through a `Converter` if the model attribute name matches the name of a
|
||||
request value such as a path variable or a request parameter (example follows).
|
||||
* Instantiated through a default constructor.
|
||||
* Instantiated through a "`primary constructor`" with arguments that match to Servlet
|
||||
request parameters. Argument names are determined through JavaBeans
|
||||
`@ConstructorProperties` or through runtime-retained parameter names in the bytecode.
|
||||
request parameters. Argument names are determined through runtime-retained parameter
|
||||
names in the bytecode.
|
||||
|
||||
One alternative to using a xref:web/webmvc/mvc-controller/ann-modelattrib-methods.adoc[@ModelAttribute method] to
|
||||
supply it or relying on the framework to create the model attribute, is to have a
|
||||
`Converter<String, T>` to provide the instance. This is applied when the model attribute
|
||||
name matches to the name of a request value such as a path variable or a request
|
||||
parameter, and there is a `Converter` from `String` to the model attribute type.
|
||||
In the following example, the model attribute name is `account` which matches the URI
|
||||
path variable `account`, and there is a registered `Converter<String, Account>` which
|
||||
could load the `Account` from a data store:
|
||||
As mentioned above, a `Converter<String, T>` may be used to obtain the model object if
|
||||
the model attribute name matches to the name of a request value such as a path variable or a
|
||||
request parameter, _and_ there is a compatible `Converter<String, T>`. In the below example,
|
||||
the model attribute name `account` matches URI path variable `account`, and there is a
|
||||
registered `Converter<String, Account>` that perhaps retrieves it from a persistence store:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -67,7 +61,6 @@ Java::
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> Bind an instance of `Account` using an explicit attribute name.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
@@ -78,50 +71,14 @@ Kotlin::
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> Bind an instance of `Account` using an explicit attribute name.
|
||||
======
|
||||
|
||||
After the model attribute instance is obtained, data binding is applied. The
|
||||
`WebDataBinder` class matches Servlet request parameter names (query parameters and form
|
||||
fields) to field names on the target `Object`. Matching fields are populated after type
|
||||
conversion is applied, 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`].
|
||||
|
||||
Data binding can result in errors. By default, a `BindException` is raised. However, to check
|
||||
for such errors in the controller method, you can add a `BindingResult` argument immediately next
|
||||
to the `@ModelAttribute`, as the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { // <1>
|
||||
if (result.hasErrors()) {
|
||||
return "petForm";
|
||||
}
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> Adding a `BindingResult` next to the `@ModelAttribute`.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String { // <1>
|
||||
if (result.hasErrors()) {
|
||||
return "petForm"
|
||||
}
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> Adding a `BindingResult` next to the `@ModelAttribute`.
|
||||
======
|
||||
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].
|
||||
|
||||
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,
|
||||
@@ -171,13 +128,48 @@ Kotlin::
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> Setting `@ModelAttribute(binding=false)`.
|
||||
<1> Setting `@ModelAt\tribute(binding=false)`.
|
||||
======
|
||||
|
||||
If data binding results in errors, by default a `MethodArgumentNotValidException` is raised,
|
||||
but you can also add a `BindingResult` argument immediately next to the `@ModelAttribute`
|
||||
in order to handle such errors in the controller method. For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { // <1>
|
||||
if (result.hasErrors()) {
|
||||
return "petForm";
|
||||
}
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> Adding a `BindingResult` next to the `@ModelAttribute`.
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
|
||||
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String { // <1>
|
||||
if (result.hasErrors()) {
|
||||
return "petForm"
|
||||
}
|
||||
// ...
|
||||
}
|
||||
----
|
||||
<1> Adding a `BindingResult` next to the `@ModelAttribute`.
|
||||
======
|
||||
|
||||
You can automatically apply validation after data binding by adding the
|
||||
`jakarta.validation.Valid` annotation or Spring's `@Validated` annotation
|
||||
(xref:core/validation/beanvalidation.adoc[Bean Validation] and
|
||||
xref:web/webmvc/mvc-config/validation.adoc[Spring validation]). The following example shows how to do so:
|
||||
`jakarta.validation.Valid` annotation or Spring's `@Validated` annotation.
|
||||
See xref:core/validation/beanvalidation.adoc[Bean Validation] and
|
||||
xref:web/webmvc/mvc-config/validation.adoc[Spring validation]. For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -210,15 +202,13 @@ Kotlin::
|
||||
<1> Validate the `Pet` instance.
|
||||
======
|
||||
|
||||
If an `@ModelAttribute` is declared without `BindingResult` parameter after it, then
|
||||
`MethodArgumentNotValueException` is raised. However, if method validation applies because
|
||||
other parameters have `@Constraint` annotations, then `HandlerMethodValidationException`
|
||||
is raised instead. For more details, see the section on
|
||||
If there is no `BindingResult` parameter after the `@ModelAttribute`, then
|
||||
`MethodArgumentNotValueException` is raised with the validation errors. However, if method
|
||||
validation applies because other parameters have `@jakarta.validation.Constraint` annotations,
|
||||
then `HandlerMethodValidationException` is raised instead. For more details, see the section
|
||||
xref:web/webmvc/mvc-controller/ann-validation.adoc[Validation].
|
||||
|
||||
TIP: Using `@ModelAttribute` is optional. By default, any parameter that is not a simple
|
||||
value type as determined by
|
||||
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty]
|
||||
_AND_ that is not resolved by any other argument resolver is treated as an `@ModelAttribute`.
|
||||
|
||||
|
||||
|
||||
@@ -1,29 +1,16 @@
|
||||
In the context of web applications, _data binding_ involves the binding of HTTP request
|
||||
parameters (that is, form data or query parameters) to properties in a model object and
|
||||
its nested objects.
|
||||
xref:core/validation/beans-beans.adoc#beans-binding[Data binding] for web requests involves
|
||||
binding request parameters to a model object. By default, request parameters can be bound
|
||||
to any public property of the model object, which means malicious clients can provide
|
||||
extra values for properties that exist in the model object graph, but are not expected to
|
||||
be set. This is why model object design requires careful consideration.
|
||||
|
||||
Only `public` properties following the
|
||||
https://www.oracle.com/java/technologies/javase/javabeans-spec.html[JavaBeans naming conventions]
|
||||
are exposed for data binding — for example, `public String getFirstName()` and
|
||||
`public void setFirstName(String)` methods for a `firstName` property.
|
||||
|
||||
TIP: The model object, and its nested object graph, is also sometimes referred to as a
|
||||
TIP: The model object, and its nested object graph is also sometimes referred to as a
|
||||
_command object_, _form-backing object_, or _POJO_ (Plain Old Java Object).
|
||||
|
||||
By default, Spring permits binding to all public properties in the model object graph.
|
||||
This means you need to carefully consider what public properties the model has, since a
|
||||
client could target any public property path, even some that are not expected to be
|
||||
targeted for a given use case.
|
||||
|
||||
For example, given an HTTP form data endpoint, a malicious client could supply values for
|
||||
properties that exist in the model object graph but are not part of the HTML form
|
||||
presented in the browser. This could lead to data being set on the model object and any
|
||||
of its nested objects, that is not expected to be updated.
|
||||
|
||||
The recommended approach is to use a _dedicated model object_ that exposes only
|
||||
properties that are relevant for the form submission. For example, on a form for changing
|
||||
a user's email address, the model object should declare a minimum set of properties such
|
||||
as in the following `ChangeEmailForm`.
|
||||
A good practice is to use a _dedicated model object_ rather than exposing your domain
|
||||
model such as JPA or Hibernate entities for web data binding. For example, on a form to
|
||||
change an email address, create a `ChangeEmailForm` model object that declares only
|
||||
the properties required for the input:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -51,13 +38,9 @@ as in the following `ChangeEmailForm`.
|
||||
}
|
||||
----
|
||||
|
||||
If you cannot or do not want to use a _dedicated model object_ for each data
|
||||
binding use case, you **must** limit the properties that are allowed for data binding.
|
||||
Ideally, you can achieve this by registering _allowed field patterns_ via the
|
||||
`setAllowedFields()` method on `WebDataBinder`.
|
||||
|
||||
For example, to register allowed field patterns in your application, you can implement an
|
||||
`@InitBinder` method in a `@Controller` or `@ControllerAdvice` component as shown below:
|
||||
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:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -74,22 +57,6 @@ For example, to register allowed field patterns in your application, you can imp
|
||||
}
|
||||
----
|
||||
|
||||
In addition to registering allowed patterns, it is also possible to register _disallowed
|
||||
field patterns_ via the `setDisallowedFields()` method in `DataBinder` and its subclasses.
|
||||
Please note, however, that an "allow list" is safer than a "deny list". Consequently,
|
||||
`setAllowedFields()` should be favored over `setDisallowedFields()`.
|
||||
|
||||
Note that matching against allowed field patterns is case-sensitive; whereas, matching
|
||||
against disallowed field patterns is case-insensitive. In addition, a field matching a
|
||||
disallowed pattern will not be accepted even if it also happens to match a pattern in the
|
||||
allowed list.
|
||||
|
||||
[WARNING]
|
||||
====
|
||||
It is extremely important to properly configure allowed and disallowed field patterns
|
||||
when exposing your domain model directly for data binding purposes. Otherwise, it is a
|
||||
big security risk.
|
||||
|
||||
Furthermore, it is strongly recommended that you do **not** use types from your domain
|
||||
model such as JPA or Hibernate entities as the model object in data binding scenarios.
|
||||
====
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user