Describe the use case
When migrating from Spring MVC to WebFlux, a common customization pattern is to subclass ModelAttributeMethodProcessor and override validateIfApplicable to apply group-based or context-aware validation — for example, selecting different @Validated groups based on the HTTP method, tenant context, or feature flags. The same need exists in WebFlux, but ModelAttributeMethodArgumentResolver does not support it.
Similarly, isBindExceptionRequired is overridden in MVC subclasses to control whether a binding/validation failure should throw or be silently passed to a BindingResult parameter — this decision sometimes needs to depend on runtime state, not just parameter inspection.
What is missing
ModelAttributeMethodProcessor (MVC) exposes both as protected extension points:
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter)
protected boolean isBindExceptionRequired(WebDataBinder binder, MethodParameter parameter)
protected boolean isBindExceptionRequired(MethodParameter parameter)
In ModelAttributeMethodArgumentResolver (WebFlux), validateIfApplicable is private and isBindExceptionRequired does not exist at all — the equivalent logic is inlined as a private hasErrorsArgument helper. Neither can be overridden by subclasses.
Proposed solution
Change validateIfApplicable from private to protected:
protected void validateIfApplicable(WebExchangeDataBinder binder,
MethodParameter parameter,
ServerWebExchange exchange)
Introduce isBindExceptionRequired as a protected method:
protected boolean isBindExceptionRequired(MethodParameter parameter)
The existing private hasErrorsArgument(MethodParameter) can be replaced by this method with no behavioral change.
Precedent in the same class
PR #24947 already established this pattern by making bindRequestParameters protected in ModelAttributeMethodArgumentResolver. The class currently exposes two protected reactive extension points:
protected Mono<Void> constructAttribute(WebExchangeDataBinder binder, ServerWebExchange exchange)
protected Mono<Void> bindRequestParameters(WebExchangeDataBinder binder, ServerWebExchange exchange)
validateIfApplicable sits at the same level in the binding lifecycle and should follow the same convention. The visibility change for validateIfApplicable keeps the synchronous signature consistent with how it is called internally — inside a .doOnSuccess() — which matches the existing contract. A separate reactive variant can be considered later if async validation becomes a requirement.
No behavioral change
Both changes are purely additive. Existing behavior is unaffected for any code that does not subclass ModelAttributeMethodArgumentResolver.
Happy to open a PR about it if the proposed solution is fine and also if there is any other approach you may want to have for this case I will be happy to help
Describe the use case
When migrating from Spring MVC to WebFlux, a common customization pattern is to subclass
ModelAttributeMethodProcessorand overridevalidateIfApplicableto apply group-based or context-aware validation — for example, selecting different@Validatedgroups based on the HTTP method, tenant context, or feature flags. The same need exists in WebFlux, butModelAttributeMethodArgumentResolverdoes not support it.Similarly,
isBindExceptionRequiredis overridden in MVC subclasses to control whether a binding/validation failure should throw or be silently passed to aBindingResultparameter — this decision sometimes needs to depend on runtime state, not just parameter inspection.What is missing
ModelAttributeMethodProcessor(MVC) exposes both as protected extension points:In
ModelAttributeMethodArgumentResolver(WebFlux),validateIfApplicableis private andisBindExceptionRequireddoes not exist at all — the equivalent logic is inlined as a privatehasErrorsArgumenthelper. Neither can be overridden by subclasses.Proposed solution
Change validateIfApplicable from private to protected:
Introduce isBindExceptionRequired as a protected method:
The existing private
hasErrorsArgument(MethodParameter)can be replaced by this method with no behavioral change.Precedent in the same class
PR #24947 already established this pattern by making
bindRequestParametersprotected inModelAttributeMethodArgumentResolver. The class currently exposes two protected reactive extension points:validateIfApplicablesits at the same level in the binding lifecycle and should follow the same convention. The visibility change forvalidateIfApplicablekeeps the synchronous signature consistent with how it is called internally — inside a.doOnSuccess()— which matches the existing contract. A separate reactive variant can be considered later if async validation becomes a requirement.No behavioral change
Both changes are purely additive. Existing behavior is unaffected for any code that does not subclass
ModelAttributeMethodArgumentResolver.Happy to open a PR about it if the proposed solution is fine and also if there is any other approach you may want to have for this case I will be happy to help