Home » Spring Boot » Request Parameter Validation in Spring Boot RESTful Service

Request Parameter Validation in Spring Boot RESTful Service

In this post, we will discuss how to validate request body and input parameter in REST API . It is important to validate incoming data in the REST web services that can cause trouble in the business process or logic. When handling input of a user, input validation is very common and important task.

For bean validation there are JSR 303 bean validation and JSR-380 specification. JSR-380 contains version 2 of the same specification. With Spring, we can utilize this specification to the fullest extent, and make validation an easier task.

Setting up a project:

To initialize a Spring boot project with bean validation,We need only web dependency. we will use Hibernate Validator Constraints which are already included in our project if you have used Spring Boot to create it and you have added spring-boot-starter-web in your pom.xml file. Since I am going to create a CRUD REST API I will add another dependencies like JPA and Mysql in pom.xml.

Tools and Technologies used for this application :

  • STS
  • Spring Boot 2.1.3.RELEASE
  • Java 1.8
  • Maven

Project Structure:

Below are the files used in this project. We will introduce them one by one.

Spring Boot Hello World

Let's Start development of the project step by step. First we need to create Spring Starter Project in STS. Follow the steps mentioned below to create the Project.

1 : Create Spring Starter Project

The very first step isto create spring starter project. So Open STS (Spring Tool Suite) then click on File -> New -> Spring Starter Project as shown below.

Spring Boot Hello World


2 : Provide basic details of the project

Next screen that will show up is given below. In that screen provide Name, Group , Artifact ,Description and Package name and click on Finish button. Here 'Artifact is name of the project and 'Group ' is the name of the base package.

Spring Boot validation


3 : Select Dependencies of Project

Click Next and we will see the following screen. At the time of writing this tutorial, the Latest release version of Spring Boot was 2.1.4, which is selected by default, so leave it as is. After that add dependencies as per requirement.Select web,h2,jpa and devtools dependency as shown below and click on Finish button.

Spring Boot validation


4 : Create JPA Entity

The first class we will create is the JPA Entity. We will create a class User with a primary key id:

package com.jwt.validation.demo.entity;

import java.util.Date;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "user")
public class User {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private String firstNane;
	private String middleName;
	private String lastName;
	private String email;
	private Date dateOfBirth;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getFirstNane() {
		return firstNane;
	}

	public void setFirstNane(String firstNane) {
		this.firstNane = firstNane;
	}

	public String getMiddleName() {
		return middleName;
	}

	public void setMiddleName(String middleName) {
		this.middleName = middleName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public Date getDateOfBirth() {
		return dateOfBirth;
	}

	public void setDateOfBirth(Date dateOfBirth) {
		this.dateOfBirth = dateOfBirth;
	}

}

5 : Create JPA Repository

package com.jwt.validation.demo.repo;

import org.springframework.data.jpa.repository.JpaRepository;

import com.jwt.validation.demo.entity.User;

public interface UserRepository extends JpaRepository {

}

6 : Create Model Request Class

package com.jwt.validation.demo.model;

import java.util.Date;

public class UserRequest {

	private String firstNane;
	private String middleName;
	private String lastName;
	private Date dateOfBirth;
	private String email;

	public String getFirstNane() {
		return firstNane;
	}

	public void setFirstNane(String firstNane) {
		this.firstNane = firstNane;
	}

	public String getMiddleName() {
		return middleName;
	}

	public void setMiddleName(String middleName) {
		this.middleName = middleName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	
	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public Date getDateOfBirth() {
		return dateOfBirth;
	}

	public void setDateOfBirth(Date dateOfBirth) {
		this.dateOfBirth = dateOfBirth;
	}

}

7 : Creating Controller

This is the spring Rest controller class which will map user request url to processing method.

package com.jwt.validation.demo.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.jwt.validation.demo.entity.User;
import com.jwt.validation.demo.model.UserRequest;
import com.jwt.validation.demo.repo.UserRepository;

@RestController
public class UserController {

	@Autowired
	UserRepository userRepository;

	@PostMapping("/createuser")
	public ResponseEntity saveUser(@RequestBody UserRequest userRequest) {
		User user = new User();
		user.setFirstNane(userRequest.getFirstNane());
		user.setMiddleName(userRequest.getMiddleName());
		user.setLastName(userRequest.getLastName());
		user.setEmail(userRequest.getEmail());
		user.setDateOfBirth(userRequest.getDateOfBirth());
		User savedUser = userRepository.save(user);
		return new ResponseEntity(savedUser, HttpStatus.CREATED);
	}

	@GetMapping("/getalluser")
	public ResponseEntity> getAllUser() {
		List findAll = userRepository.findAll();
		return new ResponseEntity>(findAll, HttpStatus.OK);
	}

}

8: Property file (application.yml)

Traditional .properties would just do fine, Spring Boot also supports YAML out of the box because SnakeYAML library is on class-path which usually would be due to starters. YAML is a superset of JSON, and as such is a very convenient format for specifying hierarchical configuration data.

spring:
  application:
    name: spring-boot-validation
  h2:
    console:
      enabled: true
  jpa:
    show-sql: true

9: Running the Application

Run the main class SpringBootValidationApplication and you will notice that jar is automatically deployed to embedded Tomcat server and Tomcat server has been started at port 8080.

Testing the APIs

We will use postman to test the API. Initially we will test post method to insert some data in H2 database.

Creating a new User using POST /createuser API

Spring Boot validation


Validate Data in H2 DB using h2 console

Launch your browser and hit the URL http://localhost:8080/h2-console/ you will get h2 console page as shown below.

Spring Boot validation

Click on connect button which will open another page. In that page you can see USER table is created.Click on the table and then click on Run button, which will display all the data available in table.Screenshot is given below.

Spring Boot validation

Our sample JSON to create the user is given below.

{
	"firstNane": "Ravi",
	"middleName": "Kumar",
	"lastName": "Raj",
	"email": "r@test.com",
	"dateOfBirth":"1987-01-23"
}

Final model Request class looks like as given below.

Currently validation is not implemented on request parameters.Ideally our request parameter must be validated so that user should allowed with only valid values.

Request body validation:

For request parameter validation, first we have to annotate our object with all the constraints and requirements. Spring has built-in support for JSR 303 Bean Validation which makes it really easy. We just annotate our fields, with NotNull, Max, Min, Pattern annotations with desired parameters.

There are many validation constraints available to use but we will use just the ones needed for this example:

  • @NotNull
  • @Size
  • @Email

@NotNull

First of all firstName, lastName, dateOfBirth and email should be required fields. Only the middleName can be left empty. To do this, we can use the @NotNull annotation:

        @NotNull
	private String firstNane;

	private String middleName;
	
	@NotNull
	private String lastName;

	@NotNull
	private String email;

	@NotNull
	private Date dateOfBirth;

Also we want to limit the lastName, middleName and firstName fields to a certain amount of characters, because In database length of these fields is limited to 30 characters. We can do that using the @Size annotation, which allows you to pass both a min and max property

        @NotNull
	@Size(min = 2, max = 25)
	private String firstNane;

	@Size(max=25)
	private String middleName;
	
	@NotNull
	@Size(min = 2, max = 25)
	private String lastName;

	@NotNull
	private String email;

	@NotNull
	private Date dateOfBirth;

We want email should be validated and this can be achieved using @Email annotation. Finally we want to make sure that the given date of birth lies in the past, not in the future. Since the 2.0 bean validation API, we can use @Past annotation for this purpose.

package com.jwt.validation.demo.model;

import java.util.Date;

import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;

public class UserRequest {

	@NotNull
	@Size(min = 2, max = 25)
	private String firstNane;

	@Size(max=25)
	private String middleName;

	@NotNull
	@Size(min = 2, max = 25)
	private String lastName;

	@NotNull
	@Email
	private String email;

	@Past
	private Date dateOfBirth;

	public String getFirstNane() {
		return firstNane;
	}

	public void setFirstNane(String firstNane) {
		this.firstNane = firstNane;
	}

	public String getMiddleName() {
		return middleName;
	}

	public void setMiddleName(String middleName) {
		this.middleName = middleName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public Date getDateOfBirth() {
		return dateOfBirth;
	}

	public void setDateOfBirth(Date dateOfBirth) {
		this.dateOfBirth = dateOfBirth;
	}

}

Updated REST Controller Endpoint:

After making changes in Model class, these validations don’t really do anything. If you would run the application, it would result in the same output as before. To solve this, we need to add the @Valid annotation to the parameters we’d like to be validated. In our Case it is UserRequest class.@Valid annotation is required to kick in the validation of the information received.

Updated end point is given below.

@PostMapping("/createuser")
	public ResponseEntity saveUser(@RequestBody @Valid UserRequest userRequest) {
		User user = new User();
		user.setFirstNane(userRequest.getFirstNane());
		user.setMiddleName(userRequest.getMiddleName());
		user.setLastName(userRequest.getLastName());
		user.setEmail(userRequest.getEmail());
		user.setDateOfBirth(userRequest.getDateOfBirth());
		User savedUser = userRepository.save(user);
		return new ResponseEntity(savedUser, HttpStatus.CREATED);
	}

Re run the application now, you can see that invalid objects is not allowed now. For example, let’s say that I forgot to pass my first name, then I’ll get the following response:

{
    "timestamp": "2019-05-08",
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "NotNull.userRequest.firstNane",
                "NotNull.firstNane",
                "NotNull.java.lang.String",
                "NotNull"
            ],
            "arguments": [
                {
                    "codes": [
                        "userRequest.firstNane",
                        "firstNane"
                    ],
                    "arguments": null,
                    "defaultMessage": "firstNane",
                    "code": "firstNane"
                }
            ],
            "defaultMessage": "must not be null",
            "objectName": "userRequest",
            "field": "firstNane",
            "rejectedValue": null,
            "bindingFailure": false,
            "code": "NotNull"
        }
    ],
    "message": "Validation failed for object='userRequest'. Error count: 1",
    "trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public org.springframework.http.ResponseEntity ....
    "path": "/createuser"
}

So,in the response we got a detailed error message describing that the first name should not be null. This much details is not required in real time project. We can customize the response which we will discuss later in this article.

Changing the error message in the response:

If you want to change default message provided by JSR Validation then you need to add a message property to the specific annotation, for example:

@NotNull(message = "First name is a required field")
@Size(min = 2, max = 25, message = "First name cannot be more than 25 characters")
private String firstName;

Run the application again by updating the code, and see the response, you’ll see that the defaultMessage has changed.

Its always a good practice to add messages in properties file or message bundles. This will be the centralize place for all of your application message. So always prefer using message bundles (messages.properties). Follow the steps mentioned below to add messages.properties file in your application.

Now we have to register our own LocalValidatorFactoryBean. LocalValidatorFactoryBean is linked to the MessageSource. This bean must be placed in any configuration file, or in our main class. If you forget to do this, you’ll see that the message is simply {user.firstName.notNull} and place holder will not be replaced by values defined in properties file.

Now Create a class called MessageConfig.java. Annotate this class with @configuration annotation so taht it will become configuration class. In the next step define LocalValidatorFactoryBean in this class.

MessageConfig.java

package com.jwt.validation.demo.config;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

@Configuration
public class MessageConfig {
	
	@Bean
	public LocalValidatorFactoryBean validator(MessageSource messageSource) {
	    LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
	    validatorFactoryBean.setValidationMessageSource(messageSource);
	    return validatorFactoryBean;
	}

}

Now run the application again and you can see messages are getting picked from messages.properties file.

Refactor messages.properties file

Now we need to refactor the properties file. If you look into key and its value for example user.firstName.size=First name cannot be longer than 20 characters. Here 20 is hardcoded in the file. Insted of hardcoding the value we can use substitutions for certain properties within our messages. For example: user.firstName.size=First name can range between {min} and {max} characters .

Run the application again and we can see desired response.

Customising the error Response:

If you see the response, it contains too much details which is not required as a user point of view. We can customize the response and prefer to have only the error message and nothing else. Only you need to do is you need to write Exception handler in controller class. Writing an exception handler can be done by creating a new method within the controller using the @ExceptionHandler annotation.

@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public ResponseEntity> handleValidationExceptions(MethodArgumentNotValidException ex) {
		Map errorMap = new HashMap<>();
		List errors = ex.getBindingResult().getAllErrors();
		for (ObjectError error : errors) {
			String fieldName = ((FieldError) error).getField();
			String errorMessage = error.getDefaultMessage();
			errorMap.put(fieldName, errorMessage);
		}
		return new ResponseEntity>(errorMap, HttpStatus.BAD_REQUEST);

	}

Updated Controller Class code is given below.

package com.jwt.validation.demo.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.jwt.validation.demo.entity.User;
import com.jwt.validation.demo.model.UserRequest;
import com.jwt.validation.demo.repo.UserRepository;

@RestController
public class UserController {

	@Autowired
	UserRepository userRepository;

	@PostMapping("/createuser")
	public ResponseEntity saveUser(@RequestBody @Valid UserRequest userRequest) {
		User user = new User();
		user.setFirstNane(userRequest.getFirstNane());
		user.setMiddleName(userRequest.getMiddleName());
		user.setLastName(userRequest.getLastName());
		user.setEmail(userRequest.getEmail());
		user.setDateOfBirth(userRequest.getDateOfBirth());
		User savedUser = userRepository.save(user);
		return new ResponseEntity(savedUser, HttpStatus.CREATED);
	}

	@GetMapping("/getalluser")
	public ResponseEntity> getAllUser() {
		List findAll = userRepository.findAll();
		return new ResponseEntity>(findAll, HttpStatus.OK);
	}
	
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public ResponseEntity> handleValidationExceptions(MethodArgumentNotValidException ex) {
		Map errorMap = new HashMap<>();
		List errors = ex.getBindingResult().getAllErrors();
		for (ObjectError error : errors) {
			String fieldName = ((FieldError) error).getField();
			String errorMessage = error.getDefaultMessage();
			errorMap.put(fieldName, errorMessage);
		}
		return new ResponseEntity>(errorMap, HttpStatus.BAD_REQUEST);

	}

}

Re run the application you will get below response which exactly we want. Provide some invalid object in JSON. For Example:

{
"firstNane": "M",
"middleName": "Kumarsdddddddddddddddadadadadadadadadadcxccxc",
"dateOfBirth": "2006-01-22",
"email":"ag.com"	
}

You will get below response.

{
    "firstNane": "First name can range between 2 and 25 characters",
    "lastName": "must not be null",
    "middleName": "size must be between 0 and 25",
    "email": "must be a well-formed email address"
}

But hold on there is some problem in our implementation by adding Exception Handler in Controller layer. The problem with this approach is code Duplication. The Exception Handler is generic and will be needed in another endpoints as well. If you want to apply this logic to all controllers, you should create a new class annotated with the @ControllerAdvice. @ControllerAdvice provides better way of handling exceptions. This is a centralized place to handle all the application level exceptions. You can read .... for more details about exception handling in restful APIs. Let us see how we can implement Exception handler using @ControllerAdvice.

Delete handleValidationExceptions method from controller and create a new class GlobalExceptionHandler and add below logic into this.

GlobalExceptionHandler.java

package com.jwt.validation.demo.exception;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

@ControllerAdvice
public class GlobalExceptionHandler {

	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public ResponseEntity> handleValidationExceptions(MethodArgumentNotValidException ex) {
		Map errorMap = new HashMap<>();
		List errors = ex.getBindingResult().getAllErrors();
		for (ObjectError error : errors) {
			String fieldName = ((FieldError) error).getField();
			String errorMessage = error.getDefaultMessage();
			errorMap.put(fieldName, errorMessage);
		}
		return new ResponseEntity>(errorMap, HttpStatus.BAD_REQUEST);

	}
}

Now this Exception handler will work for all controller for the application.

Thats all folks You can get the whole code used in this post from our GitHub repository:

https://github.com/javawebtutor/spring-boot-validation.git

Previous Next Article