-
Notifications
You must be signed in to change notification settings - Fork 38.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Built-in Web Support for Method Bean Validation #30645
Comments
Even if this issue is closed, for completeness I've added #16917 to the list of sub-tasks, and scheduled it for RC1. |
Is it possible to disable this built-in validation? I looked into the code and noticed that the HandlerMethodValidator is automatically used when In ControllerMethodResolver and in RequestMappingHandlerAdapter: private final static boolean BEAN_VALIDATION_PRESENT = ClassUtils.isPresent("jakarta.validation.Validator", HandlerMethod.class.getClassLoader()); ... if (BEAN_VALIDATION_PRESENT) {
List<HandlerMethodArgumentResolver> resolvers = this.argumentResolvers.getResolvers();
this.methodValidator = HandlerMethodValidator.from(
this.webBindingInitializer,
this.parameterNameDiscoverer,
methodParamPredicate(resolvers, ModelAttributeMethodProcessor.class),
methodParamPredicate(resolvers, RequestParamArgumentResolver.class)
);
} In HandlerMethod: /**
* Whether the method arguments are a candidate for method validation, which
* is the case when there are parameter {@code jakarta.validation.Constraint}
* annotations.
* <p>The presence of {@code jakarta.validation.Valid} by itself does not
* trigger method validation since such parameters are already validated at
* the level of argument resolvers.
* <p><strong>Note:</strong> if the class is annotated with {@link Validated},
* this method returns false, deferring to method validation via AOP proxy.
* @since 6.1
*/
public boolean shouldValidateArguments() {
return this.validateArguments;
} However, what if a developer has their own custom validation implemented via MethodInterceptor and AbstractAdvisingBeanPostProcessor? final class ControllerValidationPostProcessor extends AbstractAdvisingBeanPostProcessor {
public ControllerValidationPostProcessor(ControllerValidationInterceptor interceptor) {
super.advisor = new DefaultPointcutAdvisor(
new AnnotationMatchingPointcut(ValidController.class, true),
interceptor
);
}
} |
There isn't a way to disable it. You could declare a global |
Hey, @rstoyanchev - I appreciate the work you've put into the validation enhancements. I've had the opportunity to work with it a bit against 6.1.0 RC1. I've encountered two cases (ultimately the same root problem) where The problemEssentially, the issue I found boils down to the limited container support provided in the constructor of This leads to any violation on a nested property throwing a What I got working locallyI had some time and was interested in digging into it a bit more, so I went ahead and played with it locally to see if I could at least offer up some kind of suggestions to address this behaviour. For arrays, since they appear to produce a else if (argument instanceof Object[] array && this.containerIndex != null) {
this.container = array; // The ParameterErrors documentation currently only states container will be set for List or Map, but I figure this would be fine to set
argument = array[this.containerIndex];
} Sets were obviously a bit more awkward, since there is no direct way to access the set element that has a violation. However, it appears the public BeanResultBuilder(MethodParameter parameter, @Nullable Object argument, Path.Node node, Object leafBean){
...
else if (argument instanceof Set<?> set){ // Since there is no index or key for the container, just the container type matters
this.container = set;
argument = leafBean; // In theory this could be done for all the container types - not sure if it could be safely used for the else case, as I did not test that
}
} These tweaks allowed me to get sets and arrays to properly build and produce the desired Example snippetsBelow are some snippets that are similar to what I was using in my test cases, just to give a visual representation. If a small reproducer app would be beneficial, I can make that happen. A simple rest controller: @Controller
public class TestController {
// Produces 400 BAD REQUEST with validation error
@PostMapping("/list")
void listBody(@RequestBody @Valid List<Recipe> body) { }
// Throws org.springframework.beans.NotReadablePropertyException: Invalid property 'steps[1]' of bean class [java.util.HashSet] [...]
@PostMapping("/set")
void setBody(@RequestBody @Valid Set<Recipe> body) { }
// Throws org.springframework.beans.NotReadablePropertyException: Invalid property 'steps[1]' of bean class [[Lcom.example.TestController$Recipe;] [...]
@PostMapping("/array")
void arrayBody(@RequestBody @Valid Recipe[] body) { }
public record Recipe(
@NotBlank String title,
@NotEmpty List<@NotBlank String> steps,
Set<@Size(min = 3, max = 10) String> tags,
Map<@NotBlank String, @NotNull String> ingredients
) { }
} And this request body: [
{
"title": "A great recipe",
"steps": [ "Prep ingredients", " ", "Eat" ],
"tags": [ "food" ],
"ingredients": { "chicken": "5 wings" }
}
] |
Thanks for the feedback, @k-seth. It does look like Hibernate Validator has a It does make sense to add those changes. As you've already experimented, could you open a pull request? Or otherwise, if you don't plan to do that, then let me know or create an issue, and I'll apply the changes. |
Sure - I'll clean up my changes and do more testing, then I will get a PR up. (I have some additional work to do, the gist being the |
hey @rstoyanchev |
Hi, I think it would be very useful to disable built-in validation for specific controllers. Currently, I have an interface that describes the methods my controllers implement. In my particular case an interface is used by two Spring Boot applications, a main service that provides the actual implementation and a proxy that proxies request to the main service. Both services have controllers that implement this interface. I have constraint annotations on the method parameters of the interface. |
@mlichtblau, it's not sustainable to have follow-on requests added onto the original issue. Briefly, I'm not sure it would be ideal to have annotations specifically to turn off method validation. If your proxy does not need validation at all, make sure it doesn't declare a Jakarta |
Hi @rstoyanchev and thanks for your reply!
Sorry, makes sense. This issue #30645 is specifically mentioned in the upgrade guide for providing feedback, so I was also a bit confused why it was closed:
I opened #32292 for further discussion. |
Currently bean validation is applied to
@ModelAttribute
and@RequestBody
arguments if they are also annotated withjakarta.validation.Valid
or Spring's@Validated
with validation groups. You can put constraint annotations directly on other arguments, but that requires@Validated
on the controller class withMethodValidationPostProcessor
applying method validation via AOP.This is not intuitive and creates challenges for handling validation errors, e.g. to handle either
ConstraintViolationException
orMethodArgumentNotValidException
. Also need to introspect method parameters to know if the failure is for a request header, path variable, or other, see #26219. It can lead to double validation, see #24913. It does not support reactive types, see #20781.We need built-in method validation in web handling for a more integrated experience, default web exception handling of constraint violations to RFC 7807 responses.
This is an umbrella issue for the improvement:
ConstraintViolation
s #29825The text was updated successfully, but these errors were encountered: