Search code examples
javaspringspring-bootrequest-validation

While Doing Request Validation in Springboot, Response is not as expected


I am trying to validate the json-resquest using hibernate-validator, it is working as expected but response is not there in postman.

Customer.java

import java.time.LocalDate;
import java.util.List;

import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({ "cin", "firstName"})
public class Customer {

    @JsonProperty("cin")
    private String cin;

    @JsonProperty("firstName")
    @NotEmpty(message = "First Name must have some values")
    @Size(min = 2, message = "First Name must greater or equal to 2 characters")
    private String firstName;


//getters and setters

}

and Errors class - to wrap error in one object.

public class Errors {

    private Integer status;
    private String message;
    private List<String> details;

    public Errors(Integer status, String message, List<String> details) {
        super();
        this.status = status;
        this.message = message;
        this.details = details;
    }
    
// Getters and Setters

}

ControllerAdvice class

import java.util.List;
import java.util.stream.Collectors;

import javax.validation.ConstraintViolationException;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import com.ecommerce.ms.customer.model.Errors;

@ControllerAdvice
@ResponseBody
public class CustomerExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(value=ConstraintViolationException.class)
    public final ResponseEntity<Errors> handleConstraintViolation(ConstraintViolationException ex, WebRequest request) {
        List<String> details = ex.getConstraintViolations().parallelStream().map(e -> e.getMessage())
                .collect(Collectors.toList());
        Errors error = new Errors(HttpStatus.BAD_REQUEST.value(), "Request Validation Error", details);
        return ResponseEntity.badRequest().body(error);
    }
}

CustomerController.java

 * 
 */

import java.util.ArrayList;
import java.util.List;

import javax.validation.Valid;
import javax.ws.rs.core.MediaType;

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

import com.ecommerce.ms.customer.api.service.CustomerService;
import com.ecommerce.ms.customer.model.Customer;

@RestController
@RequestMapping("/api/customers")
public class CustomerController {

    @Autowired
    private CustomerService customerService;

    @GetMapping("/status")
    public String getStatus() {
        return "ok";
    }

    @PostMapping(consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON)
    public ResponseEntity<Customer> addCustomer(@Valid @RequestBody Customer customer) {
        return ResponseEntity.accepted().body(customerService.addCustomer(customer));
    }
}

Hibernate-validator is already added pom.xml and I am expecting the below reason.

{
   "status":400,
"message": "Request Validation Error",
"details":["First Name must greater or equal to 2 characters"]
}

Postman Request I am trying to to get proper response body but I couldn't find it in postman.


Solution

  • Looking at the ResponseEntityExceptionHandler there is no such method that handles ConstraintValidationExceptions, and therefore the custom method you have created is not being called.

    https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.html

    As well:

    You cannot catch ConstraintViolationException.class because it's not propagated to that layer of your code, it's caught by the lower layers, wrapped and rethrown under another type. So that the exception that hits your web layer is not a ConstraintViolationException.

    Ref: SpringBoot doesn't handle org.hibernate.exception.ConstraintViolationException

    An example of proper usage is to use the method handleMethodArgumentNotValid and return the Errors as body:

    @RestControllerAdvice
    public class ExceptionHandler extends ResponseEntityExceptionHandler{
    
        @Override
        protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
                                                                      HttpHeaders headers, HttpStatus status, WebRequest request) {
    
            Map<String, Object> responseBody = new LinkedHashMap<>();
    
            List<String> allErrors = new ArrayList<>();
            ex.getBindingResult().getAllErrors().forEach(error -> allErrors.add(error.getDefaultMessage()));
            responseBody.put("Errors:", allErrors);
    
            return new ResponseEntity<>(responseBody, headers, status);
        }
    
    }