This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch docs/modeldriven-parameter-binding in repository https://gitbox.apache.org/repos/asf/struts-site.git
commit 29c03826356ceef24fe53e024cad10f079bba8dc Author: Lukasz Lenart <[email protected]> AuthorDate: Wed Jul 29 09:13:14 2026 +0200 docs: document ModelDriven parameter binding and authorization An action implementing ModelDriven makes its model the target of @StrutsParameter authorization, so the model's members are bindable without the annotation. This holds on every input channel — request parameters, JSON bodies and REST bodies — since they all resolve the authorization target through the same authorizer. Document it on the @StrutsParameter page (canonical), with short notes and cross-links from the Model Driven Interceptor and REST plugin pages, plus guidance to shape a model as a request DTO rather than a domain or persistence entity. Co-Authored-By: Claude Opus 5 <[email protected]> --- source/core-developers/model-driven-interceptor.md | 6 ++++ .../core-developers/struts-parameter-annotation.md | 36 ++++++++++++++++++++++ source/plugins/rest/index.md | 7 +++++ 3 files changed, 49 insertions(+) diff --git a/source/core-developers/model-driven-interceptor.md b/source/core-developers/model-driven-interceptor.md index be88020a0..f23c50420 100644 --- a/source/core-developers/model-driven-interceptor.md +++ b/source/core-developers/model-driven-interceptor.md @@ -26,6 +26,12 @@ In the implementation of `getModel`, acquire an instance of a business object an On the page, you can address any JavaBean properties on the business object as if they were coded directly on the Action class. The framework pushes the Model object onto the ValueStack. +Pushing the model onto the stack also makes it the target of parameter binding and of `@StrutsParameter` authorization: +the model's members are populated from the request without requiring the annotation, on every input channel. Shape the +model as a request DTO holding only the fields the action intends to accept, not as a domain or persistence entity. See +[StrutsParameter Annotation](struts-parameter-annotation.html#modeldriven-actions) for the full rules. +{:.alert .alert-warning} + Many developers use Spring to acquire the business object. With the addition of a `setModel` method, the business logic can be injected automatically. diff --git a/source/core-developers/struts-parameter-annotation.md b/source/core-developers/struts-parameter-annotation.md index 33d5dfdc6..d9d46943d 100644 --- a/source/core-developers/struts-parameter-annotation.md +++ b/source/core-developers/struts-parameter-annotation.md @@ -29,6 +29,42 @@ channel that can populate an action from request data: - [JSON](../../plugins/json) and [REST](../../plugins/rest) plugins — per-property authorization performed during deserialization, so unauthorized fields are never set. +## ModelDriven actions + +When an action implements `ModelDriven` and the [Model Driven +Interceptor](model-driven-interceptor.html) has pushed the model onto the value +stack, the **model** — not the action — is the authorization target, and the +model's members are exempt from the annotation requirement. The whole model is +bindable, including its nested properties, whether or not any of its fields or +accessors carry `@StrutsParameter`. + +This follows from what the interface declares: returning an object from +`getModel()` designates that object as the request surface. The model has been +the authorization target since `@StrutsParameter` enforcement was introduced in +Struts 7.0.0. + +The exemption holds on **every** input channel listed above — request +parameters, JSON bodies and REST bodies alike — because they all resolve the +authorization target the same way. It is not a JSON- or REST-specific behavior. + +The exemption is narrowly scoped. It applies only when the action itself +implements `ModelDriven` *and* the object being populated is its model rather +than the action. A root object configured elsewhere — for example the JSON +interceptor's `root` expression on an action that does not implement +`ModelDriven` — is not exempt, and each member it binds still requires +`@StrutsParameter`. + +The exemption lifts the annotation requirement only. The other parameter-name +checks — accepted and excluded name patterns, and `ParameterNameAware` — still +apply to a model's parameters as they do to an action's. + +Because the entire model is bindable, a `ModelDriven` model should be a request +DTO carrying only the fields the action intends to accept from a request, never +a domain or persistence entity. If you need member-level control over what is +bindable, use action properties annotated with `@StrutsParameter` rather than +`ModelDriven`. +{:.alert .alert-warning} + ## Usage The placement of the `@StrutsParameter` annotation is crucial and depends on how you want to populate your action properties. diff --git a/source/plugins/rest/index.md b/source/plugins/rest/index.md index 004d21c34..ba7790930 100644 --- a/source/plugins/rest/index.md +++ b/source/plugins/rest/index.md @@ -245,6 +245,13 @@ public class OrdersController implements ModelDriven<Order> { In this example, the `ModelDriven` interface is used to ensure that only my model, the Order object in this case, is returned to the client, otherwise, the whole `OrdersController` object would be serialized. +`ModelDriven` also determines what is bindable *from* the request body: the model is the target of `@StrutsParameter` +authorization, so all of `Order` can be populated from an incoming request without the annotation. Shape such a model as +a request contract carrying only the fields the controller intends to accept, rather than as a domain or persistence +entity — see [`@StrutsParameter`](../../core-developers/struts-parameter-annotation.html#modeldriven-actions) for the +full rules. +{:.alert .alert-warning} + Where's ActionSupport? Normally, you extend ActionSupport when writing Struts 2 actions. In these case, our controller doesn't do that. Why, you ask? ActionSupport provides a bunch of important functionality to our actions, including support for i18n and validation. All of this functionality, in the RESTful case, is provided by the default interceptor stack
