I have defined a pattern for validating email in my Entity class. In my validation exception handler class, I have added handler for ConstraintViolationException. My application utilize SpringBoot 1.4.5.
Profile.java
@Entity @EntityListeners(AuditingEntityListener.class) @Table(name = "profile") public class Profile extends AuditableEntity { private static final long serialVersionUID = 8744243251433626827L; @Column(name = "email", nullable = true, length = 250) @NotNull @Pattern(regexp = "^([^ @])+@([^ \\.@]+\\.)+([^ \\.@])+$") @Size(max = 250) private String email; .... }
ValidationExceptionHandler.java
@ControllerAdvice public class ValidationExceptionHandler extends ResponseEntityExceptionHandler { private MessageSource messageSource; @Autowired public ValidationExceptionHandler(MessageSource messageSource) { this.messageSource = messageSource; } @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex, WebRequest request) { List<String> errors = new ArrayList<String>(); .... } }
When I run my code and pass invalid email address, I get the following exception. The code in handleConstraintViolation is never executed. The http status returned in the exception is 500, but I want to return 400. Any idea how I can achieve that?
2017-07-12 22:15:07.078 ERROR 55627 --- [nio-9000-exec-2] o.h.c.s.u.c.UserProfileController : Validation failed for classes [org.xxxx.common.service.user.domain.Profile] during persist time for groups [javax.validation.groups.Default, ] List of constraint violations:[ ConstraintViolationImpl{interpolatedMessage='must match "^([^ @])+@([^ \.@]+\.)+([^ \.@])+$"', propertyPath=email, rootBeanClass=class org.xxxx.common.service.user.domain.Profile, messageTemplate='{javax.validation.constraints.Pattern.message}'}] javax.validation.ConstraintViolationException: Validation failed for classes [org.xxxx.common.service.user.domain.Profile] during persist time for groups [javax.validation.groups.Default, ] List of constraint violations:[ ConstraintViolationImpl{interpolatedMessage='must match "^([^ @])+@([^ \.@]+\.)+([^ \.@])+$"', propertyPath=email, rootBeanClass=class org.xxxx.common.service.user.domain.Profile, messageTemplate='{javax.validation.constraints.Pattern.message}'}] at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:138) at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:78)
hibernate. exception. ConstraintViolationException. This is by far the most common cause of DataIntegrityViolationException being thrown – the Hibernate ConstraintViolationException indicates that the operation has violated a database integrity constraint.
Exception thrown when an action would violate a constraint on repository structure. For example, when an attempt is made to persistently add an item to a node that would violate that node's node type.
The @Valid annotation ensures the validation of the whole object. Importantly, it performs the validation of the whole object graph. However, this creates issues for scenarios needing only partial validation. On the other hand, we can use @Validated for group validation, including the above partial validation.
When Spring is configured with Hibernate, the exception is thrown in the exception translation layer provided by Spring – SessionFactoryUtils – convertHibernateAccessException. There are three possible Hibernate exceptions that may cause the DataIntegrityViolationException to be thrown:
@ExceptionHandler annotation provided by Spring Boot can be used to handle exceptions in particular Handler classes or Handler methods. Any method annotated with this is automatically recognized by Spring Configuration as an Exception Handler Method. An Exception Handler method handles all exceptions and their subclasses passed in the argument.
When Spring is configured with Hibernate, the exception is thrown in the exception translation layer provided by Spring – SessionFactoryUtils – convertHibernateAccessException. There are three possible Hibernate exceptions that may cause the DataIntegrityViolationException to be thrown: 3.2. DataIntegrityViolationException With JPA
On Running the Spring Boot Application and hitting the /getCustomer API with an Invalid Customer Id, we get a NoSuchElementException completely handled by Spring Boot as follows: Spring Boot provides a systematic error response to the user with information such as timestamp, HTTP status code, error, message, and the path.
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
.
In my case, it's a TransactionSystemException
. I'm using @Transactional
annotations from Spring with the JpaTransactionManager
. The EntityManager throws a rollback exception when somethings goes wrong in the transaction, which is converted to a TransactionSystemException
by the JpaTransactionManager
.
So you could do something like this:
@ExceptionHandler({ TransactionSystemException.class }) public ResponseEntity<RestResponseErrorMessage> handleConstraintViolation(Exception ex, WebRequest request) { Throwable cause = ((TransactionSystemException) ex).getRootCause(); if (cause instanceof ConstraintViolationException) { Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) cause).getConstraintViolations(); // do something here } }
Just want to add something. I was trying to do the same thing, validating the entity. Then I realized Spring has already everything out of the box if you validate the controller's input.
@RequestMapping(value = "/profile", method = RequestMethod.POST) public ProfileDto createProfile(@Valid ProfileDto profile){ ... }
The @Valid
annotation will trigger the validation with the javax.validation annotations.
Suppose you have a Pattern annotation on your profile username with a regexp not allowing whitespaces.
Spring will build a response with status 400 (bad request) and a body like this one:
{ "timestamp": 1544453370570, "status": 400, "error": "Bad Request", "errors": [ { "codes": [ "Pattern.ProfileDto.username", "Pattern.username", "Pattern.java.lang.String", "Pattern" ], "arguments": [ { "codes": [ "profileDto.username", "username" ], "arguments": null, "defaultMessage": "username", "code": "username" }, [], { "defaultMessage": "^[A-Za-z0-9_\\-.]+$", "arguments": null, "codes": [ "^[A-Za-z0-9_\\-.]+$" ] } ], "defaultMessage": "must match \"^[A-Za-z0-9_\\-.]+$\"", "objectName": "profileDto", "field": "username", "rejectedValue": "Wr Ong", "bindingFailure": false, "code": "Pattern" } ], "message": "Validation failed for object='profileDto'. Error count: 1", "path": "/profile" }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With