Gerrit Brehmer opened SPR-6380 and commented

I thought this was already done, because support for controller method-parameter validation should be available for all parameters.

I there a reason to exclude annotated (@RequestParam, etc.) method parameters? Is it not possible to validate primitive values?

Simple example: For Paging-Support i need two request parameters: "start" & "size". "start" must have a positive value or 0 and "size" must have a positive value and also a maximum set.


Affects: 3.0 RC2, 4.0.5

Issue Links: - #14473 Support JSR 303 @Valid on @PathVariable

75 votes, 80 watchers

Comment From: spring-projects-issues

Juergen Hoeller commented

The problem here is that JSR-303 is only really defined for bean classes: The constraint annotations are expected to sit on the target model class. As a consequence, @Valid on a parameter with primitive type doesn't mean anything since the primitive type doesn't contain any constraint annotations... Instead, we'd have to support constraint annotations like @NotNull, @Size etc themselves as parameter annotations. We can certainly consider that.

Juergen

Comment From: spring-projects-issues

Gerrit Brehmer commented

Thats sounds good, because I think same programming approach (by Annotations) for validation of input values is quite useful. Also, it would be great to be able to use SpEL-Expressions inside this validation annotations.

You could change(correct) the enhancement summary to "Support of javax.validation.*-Annotations for @PathVariable, etc.", because usage of @Valid is limited to bean classes.

Comment From: spring-projects-issues

Roel van Dijk commented

And also: the JSR-303 @Valid annotation cannot be used on method parameters:

@Target(value={METHOD,FIELD})
@Retention(value=RUNTIME)
public @interface Valid ..

The current RC2 documentation (section 5.7.4.1) still uses this as an example, but that wouldn't work, right?

Comment From: spring-projects-issues

Juergen Hoeller commented

Well, in the published version of the JSR-303 API that we're using, @Valid looks as follows:

@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface Valid {
}

So it can be used on method parameters... Which version of the API are you looking at there?

Juergen

Comment From: spring-projects-issues

Roel van Dijk commented

Ah sorry about that. I was looking at the version I had extracted locally, which is the CR1 version. I hadn't realized that the final spec was released and that different from CR1. The new spec looks great, thanks, and the example in the manual is valid.

Comment From: spring-projects-issues

Andrew Ebaugh commented

I understand the limitation of validating beans vs "primitive" types, like String. However, it currently does not appear to work for beans when using @PathVariable either.

I'd like to annotate a @Controller method like so:

@RequestMapping(value = "/user/{guid}", method = RequestMethod.GET)
public User getByGuid(@Valid @PathVariable GuidString guidString) {
}

I have already registered a converter from String->GuidString and a JSR-303 global validator that processes the GuidString validation annotations. But the validation does not appear to be done prior to invoking my handler method. It looks like the web data binder validation is only performed for method parameters annotated with @ModelAttribute.

I can see in the HandlerMethodInvoker.resolveHandlerArguments code that for this PathVariable parameter, the "validate" boolean is true because the @Valid annotation has been processed, but no validation logic is performed.

Comment From: spring-projects-issues

Kenny MacLeod commented

Take also the scenario where you have a bound model object passed in as a @RequestParam (e.g. @RequestParam MyModel model). If MyModel is annotated with JSR-303 annotations, it should be simple to pass the MyModel instance through the validator (in HandlerMethodInvoker.resolveRequestParam.

Comment From: spring-projects-issues

Gerrit Brehmer commented

Hibernate Validator added this functionality (MethodValidator) in version 4.2.0-BETA1 http://opensource.atlassian.com/projects/hibernate/browse/HV-347

Comment From: spring-projects-issues

Thad West commented

" Instead, we'd have to support constraint annotations like @NotNull, @Size etc themselves as parameter annotations. We can certainly consider that."...yes please!

Comment From: spring-projects-issues

Johan Kindgren commented

I've started to look into this feature, would it be okay to assume that "primitive" arguments are annotated with javax.validation-annotations? (More precisely, annotated with annotations that themselves are annotated with javax.validation.Constraint.) "Complex" arguments (which internally are annotated with javax.validation-annotations) should be annotated with @Valid or @Validated?

I also assume that the solution can't reference classes/interfaces from javax.validation?

Comment From: spring-projects-issues

Johan Kindgren commented

I was perhaps a bit enthusiastic about the solution. The upcoming release of Validation-api 1.1 could help a lot, but then again maybe it isn't possible to bump the validation-api version right away.

Another possible solution could be to use a bytecode modifier to create a class with the correct annotations, but that seems somewhat like a hack?

My attempted solution would add a "validate" method called from AbstractNamedValueMethodArgumentResolver.resolveArgument, seems like it could handle @RequestParameter, @RequestHeader and @PathVariable.

Comment From: spring-projects-issues

Jan Held commented

Is this topic still on the possible roadmap? I have the same problem, and fixed it for my case. I think a similar solution that Johan Kindgren proposed. Nevertheless I would prefer if this will be supported as a standard spring feature, so I can remove my workaround an simply provide custom validation Annotations and Classes.

Comment From: spring-projects-issues

Benjamin M commented

+1

Comment From: spring-projects-issues

Rossen Stoyanchev commented

The underlying implementation for this, i.e. the ability to validate method parameters, has run into unexpected challenges. For a proper resolution unfortunately we'll have to revisit in 4.2, after #16519 is resolved (see comments there for further detail).

Comment From: spring-projects-issues

roll tide commented

Is this still on roadmap?

Comment From: spring-projects-issues

Michael Pratt commented

+1, would love to see this as well.

Comment From: spring-projects-issues

Rutvij Ravi commented

In Spring 4.3, constraint annotations work on @PathVariable, @RequestHeader, and @RequestParam parameters when the Controller class is annotated with @Validated and a MethodValidationPostProcessor is registered as a Spring bean in the DispatcherServlet's application context.

Is this issue for achieving this functionality without @Validated and MethodValidationPostProcessor?

Since this functionality is usually needed in most projects, i think it makes sense for any constraints on these parameters to be validated by default.

Comment From: spring-projects-issues

Dmitry Bedrin commented

MethodValidationPostProcessor doesn't work if Controller implements any interfaces. Unless you force proxyTargetClass behavior in Spring AOP of course.

Comment From: spring-projects-issues

Juergen Hoeller commented

Along with the InvocableHandlerMethod revision for #19792, I intend to revisit this one for 5.0. We still don't have a Bean Validation API for validating individual values, however, we have the BV 1.1 ExecutableValidator API available by default now and we can probably do some smart invocation for all method arguments, triggered by a method-level @Validated annotation or possibly even just by the mere presence of parameter-level constraint annotations.

Comment From: spring-projects-issues

Thibaud Lepretre commented

Using MethodValidationPostProcessor within Spring 4.x following constraint validation works perfectly

@RestController
@Validated
@RequestMapping(path = "/api/v1/users")
class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping(path = "/")
    public CompletableFuture<Slice<UserResponse>> getUsers(
            @Min(value = 0) @RequestParam(value = "page", defaultValue = "0") int page,
            @Min(value = 1) @RequestParam(value = "limit", defaultValue = "50") int limit) {
        Pageable pageable = new PageRequest(page, limit);
        return userService.findAll(pageable)
                          .thenApply(users -> users.map(UserResponse::new));
    }

}

So is there something missing on current version of Spring? (except if Controller implements any interfaces see above)

Comment From: spring-projects-issues

Juergen Hoeller commented

This works fine for the time being indeed. I'd just like to make it more first-class, without the need for a separate post-processor and without the need for an AOP proxy to begin with. Since we directly dispatch to MVC handler methods, we can also trigger method validation directly, not having to go through a generated proxy for that purpose.

Comment From: spring-projects-issues

Thibaud Lepretre commented

Ok is clear now. Thank for information.

Comment From: spring-projects-issues

caipivara commented

Any update on this?

Comment From: spring-projects-issues

Stephano apiolaza tapia commented

It doesn´t work on websphere 8.5.11 with spring framework 4.3.20.RELEASE , the output is 

Caused by: java.lang.NoClassDefFoundError: org.hibernate.validator.method.MethodConstraintViolationException

There is a space on https://www.ibm.com/developerworks/community/forums/html/topic?id=ee47f46e-c56c-44e9-81be-0f94d4d3f1c5&ps=100&tags=&query=&filter=&sortBy=&order=asc, but the only solution that expose was https://www.ibm.com/developerworks/community/blogs/Dougclectica/entry/Spring_MVC_JSR_303_Validation_and_WebSphere?lang=en_us  

The trouble is that websphere has bean validation 1.0 and use MethodConstraintViolationException from hibernate 4 (It was deprecated), It was changed by ConstraintViolationException on hibernate 5, I Must activate bean validation 1.1 on was https://www.ibm.com/support/knowledgecenter/en/SSAW57_liberty/com.ibm.websphere.liberty.autogen.nd.doc/ae/rwlp_feature_beanValidation-1.1.html

Websphere 9 is compatible with this feature

Note: I used @validated on controller

Comment From: oberlies

Thibaud Lepretre commented So is there something missing on current version of Spring?

Yes, the status code in case of validation errors on @PathVariable, @RequestHeader, and @RequestParam parmeters is incorrect: With the described solution, there is a 500. This is different from an invalid @RequestBody, which correctly results in a 400.

Comment From: membersound

I'm having the same issue with @RestController @Validated and @GetMapping... @RequestParam. Resulting in a 500 instead of expected 400.

Comment From: rehevkor5

Is this the cause of https://github.com/spring-projects/spring-boot/issues/10471 or should a different issue be opened for that?

Comment From: chrylis

@rehevkor5 Yes, it is. I've been using Spring for 11 years now and just again got bitten by this that I'd forgotten about. The dispatcher needs to handle it in order to produce consistent error behavior.

Comment From: Bas83

I just wasted another hour on this, would love to see a solution.

Comment From: rvplauborg

So is still not possible to validate like: @PathVariable @Positive int someParam? Or what is the current status?

Comment From: pschichtel

I'm processing the exception in my custom GlobalErrorAttributes instance with this crude hack now:

private class ViolationHelper(validator: Validator) : SpringValidatorAdapter(validator) {
    @Suppress("UNCHECKED_CAST")
    fun translateViolations(violations: Set<ConstraintViolation<*>>): BindingResult {
        val target = violations.map { it.invalidValue }.firstOrNull()
        val property = violations.map { it.propertyPath }.firstOrNull()
        val errors = DirectFieldBindingResult(target, property?.toString() ?: "")
        processConstraintViolations(violations as Set<ConstraintViolation<Any>>, errors)
        return errors
    }
}

the translateViolations function gives me a a BindingResult which I can put into the error attributes similar to how it's done for normal binding errors.

It's a shame Spring still can't handle this on its own.

Comment From: marwin1991

I am also waiting for this feature

Comment From: marwin1991

Any progress?

Comment From: rstoyanchev

This has been a long running issue, at first not possible until Hibernate Validator added method level validation, and later enabled in Spring Framework 4.x through a MethodValidationPostProcessor, see https://github.com/spring-projects/spring-framework/issues/11041#issuecomment-453343899. We intend to add built-in support at the web framework level with #24913, so I'm closing this in favor of that.