When developing REST Web Services, you should validate the input parameters and return a 4xx response in case of invalid data. The body of the response should contain field-wise error details in a structured format, so that clients can show that to the end-users nicely.

Let’s take an example. Say we have a signup form, as below:

public class SignupForm {

    public String email;
    public String name;
    public String password;

    // getters and setters
}

And, we receive the input in a controller method, as below:

@RestController("/users")
public class UserController {

    @Autowired
    UserService userService;

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public UserResource signup(@RequestBody SignupForm user) {
        return userService.signup(user);
    }
}

The service method that’s being called above could look as below:

@Service
public class UserService {

    public UserResource signup(SignupForm user) {
        // save the user
        // return userResource;
    }
}

So, if a field is blank, how to send an error response?

A good way for this -- when using Spring Boot and MVC -- is to use JSR-303/JSR-349 annotations in the form, and use @Valid in the controller. That would throw an exception when there’ll be some error. We can then use Spring’s exception handling mechanisms to handle that and send the error response.

For example, our signup form could be modified as follows:

public class SignupForm {

    @NotBlank
    @Email
    @Size(min=4, max=200)
    public String email;

    @NotBlank
    @Size(min=1, max=100)
    public String name;

    @NotBlank
    @Size(min=6, max=30)
    public String password;

    // getters and setters    
}

And, the parameter in the controller could be annotated with @Valid:

@RestController("/users")
public class UserController {

    @Autowired
    UserService userService;

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public UserResource signup(@RequestBody @Valid SignupForm user) {
        return userService.signup(user);
    }
}

Now, in case of any errors, Spring would throw an exception instead of executing the controller code. The exception can then be handled in a cross-functional way.

So far so good. But, some people prefer to do the validation in the service layer. Validation is business logic, after all, and so service layer seems ideal for it. Doing it in the service layer also allows us to do some pre-processing in the controller - see this for a use case.

Spring Boot supports that very well. Instead of using the @Valid annotation in the controller, you’ll need to do it in the service, as below:

@Service
@Validated
public class UserService {

     public UserResource signup(@Valid SignupForm user) {
        // save the user
        // return userResource;
    }
}

Don’t miss the class level @Validated annotation.

So, how does it work?

Spring Boot auto configures a MethodValidationPostProcessor component, which validates all method parameters that are annotated with @Valid inside all components that are annotated with @Validated. It throws a ConstraintViolationException exception in case of errors.

Your job then becomes handling the exception and translating that to a suitable error response. While we've already discussed how to handle exceptions in another post, let's now discuss how to translate a ConstraintViolationException to a suitable response body.

How to translate ConstraintViolationExceptions

Get the validation errors

ConstraintViolationException’s getConstraintViolations() method returns the errors as a set of ConstraintViolations. So, we should transform ex.getConstraintViolations() into a simple structure and return that in the response.

Structure to send those to the client

A simple structure for returning the validation errors to the client could look like this:

[
    {
        field: "user.email",
        code: "com.naturalprogrammer.spring.invalid.email",
        message: "Not a well formed email address"
    },
    {
        field: "user.email",
        code: "com.naturalprogrammer.spring.blank.email",
        message: "Email needed"
    },
    {
        field: "user.name",
        code: "blank.name",
        message: "Name required"
    },
    {
        field: "user", // OR null OR "something.unknown"
        code: "some.code",
        message: "Some form level error"
    },
    {
        field: "user.address.lane",
        code: "com.naturalprogrammer.spring.long.lane",
        message: "Lane must be less than 50 characters"
    }
]

This is an array of error messages. Each error message has

  1. A field containing the path of the field. Nested fields can have multiple dots in them, like "user.address.lane" in the fifth case above. In case the error is not related to a specific field, but is a form-level global error, as in the fourth case above, field can be just the object name, null, or something unknown.
  2. A code containing the error code.
  3. A message containing the internationalized error message.

The error messages in the above array can be structured as a simple class as below:

public class FieldError {

    private String field;
    private String code;
    private String message;

    ... getters and other utility methods ...
}

Converting ConstraintViolations to FieldErrors

ConstraintViolation has the following attributes:

{
   propertyPath: "signup.user.email", 
   messageTemplate: "error-message-code", 
   message: "Not a valid email"
   ...
}

The propertyPath above is of the format methodName.objectName.fields. We won’t need the methodName prefix when sending it to the client. To strip it out, the code snippet below can be used:

String field = StringUtils.substringAfter(
    constraintViolation.getPropertyPath().toString(), ".");

So, in summary, the method below would convert a ConstraintViolation to a FieldError:

private static FieldError of(ConstraintViolation<?> constraintViolation) {

    String field = StringUtils.substringAfter(
                constraintViolation.getPropertyPath().toString(), ".");

    return new FieldError(field,
            constraintViolation.getMessageTemplate(),
            constraintViolation.getMessage());      
}

Using the above method, a set of ConstraintViolations can then be converted to a list of FieldErrors as below:

public static List<FieldError> getErrors(Set<ConstraintViolation<?>> constraintViolations) {

    return constraintViolations.stream()
        .map(FieldError::of)
        .collect(Collectors.toList());  
}

To see the exact details, look at the FieldError and the DefaultExceptionHandler classes in Spring Lemon.

Multiple ways to use method validation

Method validation can be used in multiple ways. See the three examples below:

@Validated
@Service
public class MyService {

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

    public void secondMethod(@Valid @Email @NotBlank String email) {
        ... 
    }

    @Validated(SignUpValidation.class)  
    public void thirdMethod(@Valid User user) {
        ... 
    }
}

In the first method, we are validating a User object. In the second method, we are validating a primitive string. In the third method, we are providing a constraint group by using the @Validated(SignUpValidation.class) annotation on the method. That is telling Spring to validate only those fields that have the SignUpValidation.class in their groups, like this:

public class User {

    @Size(min=1, max=30, groups = {SignUpValidation.class, UpdateValidation.class})
    private String name;
}

The SignUpValidation.class and UpdateValidation.class are nothing but just marker interfaces.

Using constraint groups this way is helpful when reusing the same command class for multiple use cases.

Summary

Following the above pattern, you can write clean business code, without bothering about how to send precise error responses to the client. In a next post, we'll discuss how handle custom errors, which'll be equally useful. So, stay tuned!

Be a master of such real-world patterns by taking our Mastering Spring RESTful Web Services Development video tutorial, part I of which is FREE now!