Saturday, April 18, 2020

Spring REST Request Body and Parameter Validation Example

Learn to validate the request body (JSON posted to Spring REST API). Also validate @PathVariable and @RequestParam parameters in resource URIs using hibernate validator 2.x.
In this spring rest validation example, we will be adding validations in REST APIs created for CRUD example.

1. Request body validation using hibernate validator

1.1. Maven dependency

pom.xml
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.16.Final</version>
</dependency>
This transitively pulls in the dependency to the Bean Validation API (javax.validation:validation-api:2.0.1.Final).

1.2. Enable bean validation

In Spring boot, the bean validation is automatically enabled if any JSR-303 implementation (like hibernate validator 2.0) is available on the classpath.
If we are not using spring boot, we need to add LocalValidatorFactoryBean.
Java Config
@Bean
public javax.validation.Validator localValidatorFactoryBean() {
    return new LocalValidatorFactoryBean();
}
XML Config
<bean id="validator"
    class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

1.3. Add bean validation annotations

Add the bean validation annotations in model classes which will store the request body data such as @NotEmpty and @Email.
Java Config
import java.io.Serializable;
 
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
 
@Entity
@Table(name = "tbl_employee")
public class Employee implements Serializable
{
    private static final long serialVersionUID = 1L;
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
 
    @NotEmpty(message = "First name must not be empty")
    private String firstName;
 
    @NotEmpty(message = "Last name must not be empty")
    private String lastName;
 
    @NotEmpty(message = "Email must not be empty")
    @Email(message = "Email must be a valid email address")
    private String email;
 
    public Employee() {
    }
 
    //setters and getters
 
    @Override
    public String toString() {
        return "EmployeeVO [id=" + id + ", firstName=" + firstName + ",
                lastName=" + lastName + ", email=" + email + "]";
    }
}

1.4. Handle ConstraintViolationException

In case of any validation failures, Spring will throw ConstraintViolationException. We can handle it any return a meaningful JSON error response from using @ExceptionHandler.
CustomExceptionHandler.java
@ControllerAdvice
@ResponseBody
public class CustomExceptionHandler extends ResponseEntityExceptionHandler
{
    @ExceptionHandler(ConstraintViolationException.class)
    public final ResponseEntity<ErrorResponse> handleConstraintViolation(
                                            ConstraintViolationException ex,
                                            WebRequest request)
    {
        List<String> details = ex.getConstraintViolations()
                                    .parallelStream()
                                    .map(e -> e.getMessage())
                                    .collect(Collectors.toList());
 
        ErrorResponse error = new ErrorResponse(BAD_REQUEST, details);
        return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
    }
}

2. Query and path parameter validation

In Spring REST, parameters in request URI are captured via @PathVariable and all query parameters via @RequestParam.
Please note that maven dependency shall be added and ConstraintViolationException should be handled as described above.

2.1. Enable validation

Query and path parameter validation is not straightforward. We need to explicitly create bean MethodValidationPostProcessor which will process the @Validated annotation.
XML Config
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
Java Config
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
     return new MethodValidationPostProcessor();
}

2.2. Add validation to parameters

  • use JSR-303 annotations as described above.
  • use @Validated annotation on top of controller so it is applicable to all methods in it.
@RestController
@RequestMapping(value = "/employee-management",
        produces = { MediaType.APPLICATION_JSON_VALUE })
@Validated
public class EmployeeRESTController
{
    @GetMapping("/employees/{id}")
    Employee getEmployeeById(@PathVariable
                             @Min(value = 1, message = "id must be greater than or equal to 1")
                             @Max(value = 1000, message = "id must be lower than or equal to 1000") Long id)
    {
        return repository.findById(id)
                .orElseThrow(() -> new RecordNotFoundException("Employee id '" + id + "' does no exist"));
    }
}

3. Demo

3.1. Request body validation

//1
API Request
HTTP POST : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
 
Headers:
 
AUTH_API_KEY: abcd123456
Content-Type: application/json
 
Body:
 
{
    "firstName": "",
    "lastName": "Gupta",
    "email": "abc@gmail.com"
}
API Response
{
    "message":"BAD_REQUEST",
    "details":["First name must not be empty"]
}
//2
API Request
HTTP POST : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/
 
Headers:
 
AUTH_API_KEY: abcd123456
Content-Type: application/json
 
Body:
 
{
    "firstName": "",
    "email": "abc@gmail.com"
}
API Response
{
    "message":"BAD_REQUEST",
    "details":
            [
                "First name must not be empty",
                "Last name must not be empty"
            ]
}

3.2. Path param validation

//1
API Request
HTTP GET : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/0
 
Headers:
 
AUTH_API_KEY: abcd123456
Content-Type: application/json
API Response
{
    "message":"BAD_REQUEST",
    "details":["id must be greater than or equal to 1"]
}
 
//2
 
 
HTTP GET : http://localhost:8080/SpringRestExample/api/rest/employee-management/employees/5000
 
Headers:
 
AUTH_API_KEY: abcd123456
Content-Type: application/json
{
“message”:”BAD_REQUEST”,
“details”:[“id must be lower than or equal to 1000”]
}

No comments:

Post a Comment

How to DROP SEQUENCE in Oracle?

  Oracle  DROP SEQUENCE   overview The  DROP SEQUENCE  the statement allows you to remove a sequence from the database. Here is the basic sy...