Skip to content
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

Bean Validation invocation API for use with individual values and constraints [SPR-11900] #16519

Open
spring-projects-issues opened this issue Jun 23, 2014 · 7 comments
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Jun 23, 2014

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:

10 votes, 12 watchers

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jun 30, 2014

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

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jun 30, 2014

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

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Feb 14, 2015

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);
        }
    }
}

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Feb 14, 2015

Sam Brannen commented

Thanks for sharing, Baron!

The API provided via ExecutableValidator does indeed look promising.

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jun 19, 2015

Baron Roberts commented

Any update on this issue?

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Sep 22, 2015

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).

@spring-projects-issues
Copy link
Collaborator Author

spring-projects-issues commented Jan 25, 2017

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has: votes-jira Issues migrated from JIRA with more than 10 votes at the time of import in: core Issues in core modules (aop, beans, core, context, expression) type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

2 participants