Issue
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"]
}
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.
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);
}
}
Answered By - SMA