Spring Validation enables you to use annotations and interfaces to simplify validation logic. This tutorial provides examples of how to do a few “real-world” validations for a JSON API.

After completing this tutorial, you’ll know how to

Validate Java objects using custom and built-in validators and annotations

Handle validation exceptions and present the errors to client-side applications

You’ll be able to write code like:

@Min ( 0 ) private Integer price ;

and get JSON responses like:

{ "field" : "price" , "code" : "Min" , "rejectedValue" : -3 }

Browse the completed version of this tutorial on GitHub: SpringValidationExample

If you already depend on spring-boot-starter-web , then you already have Spring Validation. Otherwise, you’ll have to include org.springframework:spring-context . Annotate the argument you want validated with the @Valid annotation. In this case we’re validating an API request to create a product in our system (in ProductController.java). @RequestMapping ( value = "/products" , method = RequestMethod . POST ) public Product create ( @Valid @RequestBody ProductCreateRequest productCreateRequest ) { Product product = productCreateRequest . toProduct (); productRepository . save ( product ); return product ; } class ProductCreateRequest { private String name ; private String sku ; private Integer price ; ... } public class Product { private String sku ; private String name ; private Integer price ; private LocalDateTime createdAt ; ... } Create a class that implements org.springframework.validation.Validator . (Don’t confuse this with javax.validation.Validator .) import org.springframework.validation.Validator ; @Component public class ProductCreateRequestValidator implements Validator { private ProductRepository productRepository ; @Autowired public ProductCreateRequestValidator ( ProductRepository productRepository ) { this . productRepository = productRepository ; } @Override public boolean supports ( Class <?> clazz ) { return ProductCreateRequest . class . isAssignableFrom ( clazz ); } @Override public void validate ( Object target , Errors errors ) { ProductCreateRequest productCreateRequest = ( ProductCreateRequest ) target ; if ( productRepository . exists ( productCreateRequest . toProduct ())) { errors . reject ( ALREADY_EXISTS . getCode ()); } } } Tell Spring to bind the validator to data from web requests. In ProductController , autowire the validator and create a method annotated with @InitBinder("productCreateRequest") . (Note: specify the name of the request parameter - otherwise Spring gets confused with multiple binders) private ProductCreateRequestValidator productCreateRequestValidator ; @Autowired public ProductController ( ProductCreateRequestValidator productCreateRequestValidator ) { this . productCreateRequestValidator = productCreateRequestValidator ; } @InitBinder ( "productCreateRequest" ) public void setupBinder ( WebDataBinder binder ) { binder . addValidators ( productCreateRequestValidator ); } So far, we’ve validated uniqueness of a product by checking if it already exists in our database. To validate that the product has a name and SKU ( @NotBlank ) and a non-negative price ( @Min(0) ), we can simply annotate the ProductCreateRequest . import org.hibernate.validator.constraints.NotBlank ; import javax.validation.constraints.Min ; class ProductCreateRequest { @NotBlank private String name ; @NotBlank private String sku ; @Min ( 0 ) private Integer price ; ... } Unfortunately, if we send invalid data to the /products endpoint, Spring will respond with a verbose, unhelpful error message. To have the application to respond with a useful, consistent error message, Spring must intercept the MethodArgumentNotValidException caused by the invalid data, and format it. Create a @ControllerAdvice class to handle this exception universally. @ControllerAdvice public class ApiValidationExceptionHandler extends ResponseEntityExceptionHandler { @Override protected ResponseEntity < Object > handleMethodArgumentNotValid ( MethodArgumentNotValidException ex , HttpHeaders headers , HttpStatus status , WebRequest request ) { BindingResult bindingResult = ex . getBindingResult (); List < ApiFieldError > apiFieldErrors = bindingResult . getFieldErrors () . stream () . map ( fieldError -> new ApiFieldError ( fieldError . getField (), fieldError . getCode (), fieldError . getRejectedValue ()) ) . collect ( toList ()); List < ApiGlobalError > apiGlobalErrors = bindingResult . getGlobalErrors () . stream () . map ( globalError -> new ApiGlobalError ( globalError . getCode ()) ) . collect ( toList ()); ApiErrorsView apiErrorsView = new ApiErrorsView ( apiFieldErrors , apiGlobalErrors ); return new ResponseEntity <>( apiErrorsView , HttpStatus . UNPROCESSABLE_ENTITY ); } } public class ApiErrorsView { private List < ApiFieldError > fieldErrors ; private List < ApiGlobalError > globalErrors ; ... } public class ApiFieldError { private String field ; private String code ; private Object rejectedValue ; ... } public class ApiGlobalError { private String code ; ... } When the application receives a request with invalid data, it will respond with a status code of 422 (Unprocessable Entity) and a nice JSON description of why. { "fieldErrors" : [ { "field" : "price" , "code" : "Min" , "rejectedValue" : -3 } ], "globalErrors" : [ { "code" : "AlreadyExists" } ] } Browse the completed code on GitHub: SpringValidationExample

The Spring Validation Documentation explains these concepts further.

How are you doing validation on your Spring project? Let me know in the comments.