mscharhag, Programming and Stuff;

A blog about programming and software development topics, mostly focused on Java technologies including Java EE, Spring and Grails.

Monday, 6 January, 2020

Method parameter validation with Spring and JSR 303

Spring provides an easy way to validate method parameters using JSR 303 bean validation. In this post we will see how to use this feature.

Setup

First we need to add support for method parameter validation by creating a MethodValidationPostProcessor bean:

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

Validating method parameters

After registering the MethodValidationPostProcessor we can enable method parameter validation per bean by adding the @Validated annotation. Now we can add Java Bean validation annotations to our method parameter to perform validation.

@Service
@Validated
public class UserService {

    public User getUser(@NotBlank String uuid) {
        ...
    }
}

Here we added a @NotBlank annotation to make sure the passed uuid parameter is not null or an empty string. Whenever an invalid uuid is passed a ContraintViolationException will be thrown.

Besides simple parameter validation we can also validate objects annotated with JSR 303 annotations.

For example:

public class User {

    @NotBlank
    private String name;

    // getter + setter
}
@Service
@Validated
public class UserService {

    public void createUser(@Valid User user) {
        ...
    }
}

By adding @Valid (not @Validated) we mark the user parameter for validation. The passed user object will then be validated based on the validation constraints defined in the User class. Here the name field should not be null or contain an empty string.

Note that validation also works for Controller method parameters. You can use this to validate path variables, request parameters or other controller method parameters.

For example:

@RestController
@Validated
public class UserController {

    @GetMapping("/users/{userId}")
    public ResponseEntity<User> getUser(
        @PathVariable @Pattern(regexp = "\\w{2}\\d{8}") String userId
    ) {
        // ...
    }
}

Here we use the @Pattern annotation to validate the path variable userId with an regular expression.

How does this work?

The MethodValidationPostProcessor bean we registered is a BeanPostProcessor that checks each bean if it is annotated with @Validated. If this is the case Spring creates a Proxy objects and registers AOP interceptor (MethodValidationInterceptor) to intercept method calls and performs validation. The actual bean method is only called if the validation was successful.

Limitations

Because this feature relies on AOP interceptors it works only on Spring managed beans. It also only works if the method with validation annotations is called from outside the class.

Let's look at an example to better understand this:

@Service
@Validated
public class UserService {

    public void updateUsername(String uuid, String newName) {
        User user = getUser(uuid); // no validation

        // ...
    }

    public User getUser(@NotBlank String uuid) {
        return new User("John");
    }
}

Here the getUser(..) method is called inside the updateUsername(..) method. Therefore, the validation of the uuid parameter in getUser(..) is not triggered. There is no AOP proxy involved here.

Outside classes usually access the class via a reference retrieved through Spring Dependency Injection. In this case, Spring injects the proxy object and everything is works as expected.

 

As always you can find the sources for the shown examples on GitHub.

Leave a reply