Rossen Stoyanchev opened SPR-11900 and commented
The logic in MethodValidationInterceptor is pretty useful. However it is currently not easy to use if not in the context of AOP method interception. I'd like to be able to use it for invoking an @MVC
HandlerMethod with the ability to validate method arguments first and then the method return value as a separate step.
Affects: 4.0.5
Issue Links:
- #15017 Random results for JSR-303 method constraint validation on generically typed methods
- #18007 @Validated
support on Iterables (and implementors)
- #19182 Validate values in top-level Map parameters
10 votes, 12 watchers
Comment From: spring-projects-issues
Juergen Hoeller commented
As per our discussion today, we're going to revisit this from a slightly different perspective: Calling a Bean Validation provider for individual values to validate, given a set of constraint annotations... Those may come from a specific method parameter but also from a field or the like.
Reusing the method validation feature in Bean Validation 1.1 is not going to get us there since it doesn't allow for the individual validation of parameters, just for validating all parameters at once. This proves to be problematic with our existing support for @Valid
beans, as well as with finding out which exact parameter failed to validate (since no parameter index is being exposed by BV 1.1, just the failing value).
Juergen
Comment From: spring-projects-issues
Juergen Hoeller commented
Looking at the inner APIs of Hibernate Validator 5, there doesn't seem to be an easy way to validate a single value within the context of a specific method parameter at all. Ideally, we'd either get such an API or even a super-plain API that just takes a value to validate plus an array of annotations, independent from the source of those annotations...
Ironically, Hibernate Validator 4.3 did provide a proprietary validateParameter method for an individual parameter. However, that API is deprecated there, so nothing to base a new Spring feature on if there's no equivalent in Hibernate Validator 5. If we get some such API in Hibernate Validator 5, we could support the Hibernate Validator 4.3 variant as a fallback.
Juergen
Comment From: spring-projects-issues
Baron Roberts commented
I was able to achieve controller parameter validation using the forExecutables()
method on the JSR 303 validator within an aspect. Since Juergen raised the question of available validation API's in Hibernate Validator 5, I thought you folks might find this useful.
First I created a custom annotation that will be placed on the controller methods I wish to be validated. I could have accomplished the validation without the use of the annotation and applied the advice to all controller methods but I wanted the ability to be more selective. Here is the code for the annotation:
package com.cthing.validation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a controller method whose parameters are to be validated using
* Java and Hibernate validators.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ValidateParams {
}
The heart of the solution is the validation aspect. I apply the advice to any controller method that has the above annotation. Using a JSR 303 ValidatorFactory
, I obtain an ExecutableValidator
and use its validateParameters()
method to validate the method parameters provided by the aspect. Here is the code for the aspect:
package com.cthing.controller;
import java.lang.reflect.Method;
import java.util.Set;
import javax.inject.Inject;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
/**
* Performs validation on controller parameters annotated with {@link com.cthing.validation.ValidateParams}.
*/
@Aspect
@Component
public class ValidateParamsAdvice {
private final ValidatorFactory validatorFactory;
@Inject
public ValidateParamsAdvice(final ValidatorFactory jsr303ValidationFactory) {
this.validatorFactory = jsr303ValidationFactory;
}
/**
* Validates controller method parameters.
*
* @param joinPoint Matched method join point
*/
@Before("execution(public * com.cthing.controller.*.*(..)) && @annotation(com.cthing.validation.ValidateParams)")
public void validateMethodParams(final JoinPoint joinPoint) {
final Object controller = joinPoint.getThis();
final Method controllerMethod = ((MethodSignature)joinPoint.getSignature()).getMethod();
final Object[] params = joinPoint.getArgs();
final ExecutableValidator executableValidator = this.validatorFactory.getValidator().forExecutables();
final Set<ConstraintViolation<Object>> violations =
executableValidator.validateParameters(controller, controllerMethod, params);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}
Comment From: spring-projects-issues
Sam Brannen commented
Thanks for sharing, Baron!
The API provided via ExecutableValidator
does indeed look promising.
Comment From: spring-projects-issues
Baron Roberts commented
Any update on this issue?
Comment From: spring-projects-issues
Juergen Hoeller commented
The problem with the Bean Validation 1.1 ExecutableValidator
API is that it has only been designed for validating all parameters of a given method/constructor at once. What we need for our MVC argument handling is an API for validating a single parameter value as an individual step, picking up the constraint metadata from a specified method/constructor... and I'm still not seeing this anywhere in Hibernate Validator 5.x.
I suppose we need to get in touch with the HV team and ask for introducing such a validateValue
variant for method/constructor use in Hibernate Validator 5.3, since this request doesn't seem to be a bad fit with the current issues planned there (https://hibernate.atlassian.net/projects/HV/versions/21451).
Comment From: spring-projects-issues
Filip Panovski commented
Hi, has there been any update on this issue? Is it still pending Hibernate updates, or has it been postponed indefinitely? Definitely a very nice feature that would make Controller
code more readable and streamilined. I unfortunately cannot open the Hibernate project ticket linked in the last comment, so I am not sure what the status there is.
Comment From: jhoeller
Superseded by #30645.