24

I have tried a number of examples from the net and cannot get Spring to validate my query string parameter. It doesn't seem execute the REGEX / fail.

package my.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import javax.validation.constraints.Pattern;

import static org.springframework.web.bind.annotation.RequestMethod.GET;

@RestController
public class MyController {

    private static final String VALIDATION_REGEX = "^[0-9]+(,[0-9]+)*$";

    @RequestMapping(value = "/my/{id}", method = GET)
    public myResonseObject getMyParams(@PathVariable("id") String id,
                                       @Valid @Pattern(regexp = VALIDATION_REGEX) 
                                       @RequestParam(value = "myparam", required = true) String myParam) {
         // Do Stuff!
    }

}

Current behaviour

PASS - /my/1?myparam=1
PASS - /my/1?myparam=1,2,3
PASS - /my/1?myparam=
PASS - /my/1?myparam=1,bob

Desired behaviour

PASS - /my/1?myparam=1
PASS - /my/1?myparam=1,2,3
FAIL - /my/1?myparam=
FAIL - /my/1?myparam=1,bob

Thanks

ptimson
  • 5,533
  • 8
  • 35
  • 53

2 Answers2

58

You need add @Validated to your class like this:

@RestController
@Validated
class Controller {
  // ...
}

UPDATE:

you need to configure it properly.. add this bean to your context:

@Bean
 public MethodValidationPostProcessor methodValidationPostProcessor() {
      return new MethodValidationPostProcessor();
 }

Example to handle exception:

@ControllerAdvice
@Component
public class GlobalExceptionHandler {
    @ExceptionHandler
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map handle(MethodArgumentNotValidException exception) {
        return error(exception.getBindingResult().getFieldErrors()
                .stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.toList()));
    }


    @ExceptionHandler
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map handle(ConstraintViolationException exception) {
        return error(exception.getConstraintViolations()
                .stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.toList()));
    }

    private Map error(Object message) {
        return Collections.singletonMap("error", message);
    }
}
Jaiwo99
  • 9,687
  • 3
  • 36
  • 53
  • 1
    Thanks but still nothing :( I added org.springframework.validation.annotation.Validated; – ptimson Jul 27 '16 at 14:10
  • This gives me a runtime error on request: 2016-07-27 15:37:01.444 ERROR 10611 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.validation.ConstraintViolationException] with root cause javax.validation.ConstraintViolationException: null at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:136) ~[spring-context-4.2.7.RELEASE..... – ptimson Jul 27 '16 at 14:38
  • @ptimson this is 'ConstraintViolationException', which you are expecting.. it basically says: `argument invalid`, on the other word, it works! – Jaiwo99 Jul 27 '16 at 14:49
  • @ptimson if you have a rest interface, you need to handle the exception, paste you an example here. – Jaiwo99 Jul 27 '16 at 14:51
  • sorry yes you are right it works :) I thought it would be caught as a normal 400 error. How can I do this? What would you like to see? – ptimson Jul 27 '16 at 14:53
  • @ptimson checkout the example, standard should be 400 or 426 as response – Jaiwo99 Jul 27 '16 at 14:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/118457/discussion-between-ptimson-and-jaiwo99). – ptimson Jul 27 '16 at 15:06
  • 1
    Two comments for others having the same problem as me: 1. I didn't need the `MethodValidationPostProcessor` - guessing maybe I'm getting it for free from something else, but I haven't checked. 2. I think it's important that all the validation-related annotations are on the actual `@RestController` - I was trying to use them in an interface that my controller implemented, with no luck. (Not a pattern I'd choose, but one I've inherited!) – DaveyDaveDave Jun 28 '17 at 12:31
  • 3
    I can confirm that `MethodValidationPostProcessor` is not needed (tested with Spring Boot 1.5.10) – Wim Deblauwe Mar 07 '18 at 16:57
  • @WimDeblauwe this post ist from 2016, and you still need `MethodValidationPostProcessor`, but you didn't notice it because spring boot created the bean for you – Jaiwo99 Mar 07 '18 at 20:34
  • That is what I meant, you don’t need to define it yourself. – Wim Deblauwe Mar 07 '18 at 21:01
2

You can try this

@Pattern(regexp="^[0-9]+(,[0-9]+)*$")
private static final String VALIDATION_REGEX;

(pay attention for the final modifier) or else

 @Pattern()
 private static final String VALIDATION_REGEX = "^[0-9]+(,[0-9]+)*$";

And then remove @Pattern(regexp = VALIDATION_REGEX) from your method and keep only the @Valid annotation:

public myResonseObject getMyParams(@PathVariable("id") String id, @Valid @RequestParam(value = "myparam", required = true) String myParam) {
AchillesVan
  • 4,156
  • 3
  • 35
  • 47