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 appli
@ResponseBody
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ExceptionHandler(DataIntegrityViolationException.class)
public Map errorHandler(DataIntegrityViolationException ex) {
Map map = new HashMap();
map.put("rs_code", 422);
map.put("rs_msg", "data existed !");
return map;
}
just catch org.springframework.dao.DataIntegrityViolationException
.
You can handle org.hibernate.exception.ConstraintViolationException by adding this in your @controllerAdvice
@ExceptionHandler(DataIntegrityViolationException.class) public ResponseEntity handleConstraintViolationException(Exception ex){
String errorMessage = ex.getMessage();
errorMessage = (null == errorMessage) ? "Internal Server Error" : errorMessage;
List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
return new ResponseEntity<ErrorResponseDTO>(
new ErrorResponseDTO( errorMessage ,details), HttpStatus.INTERNAL_SERVER_ERROR);
}
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"
}
Just check all Exceptions and select the one you need
Need to determine the cause:
while ((cause = resultCause.getCause()) != null && resultCause != cause) {
resultCause = cause;
}
Use instanceof
@ExceptionHandler(Exception.class)
protected ResponseEntity<MyException> handleExceptions(Exception e) {
String message;
Throwable cause, resultCause = e;
while ((cause = resultCause.getCause()) != null && resultCause != cause) {
resultCause = cause;
}
if (resultCause instanceof ConstraintViolationException) {
message = (((ConstraintViolationException) resultCause).getConstraintViolations()).iterator().next().getMessage();
} else {
resultCause.printStackTrace();
message = "Unknown error";
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new MyException(message));
}
That is my solution...
@ExceptionHandler({DataIntegrityViolationException.class})
protected ResponseEntity<Object> handlePersistenceException(final DataIntegrityViolationException ex) {
Throwable cause = ex.getRootCause();
if (cause instanceof SQLIntegrityConstraintViolationException) {
SQLIntegrityConstraintViolationException consEx = (SQLIntegrityConstraintViolationException) cause;
final ApiErrorResponse apiError = ApiErrorResponse.newBuilder()
.message(consEx.getLocalizedMessage())
.status(HttpStatus.BAD_REQUEST)
.build();
return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
}
final ApiErrorResponse apiError = ApiErrorResponse.newBuilder()
.message(ex.getLocalizedMessage())
.status(HttpStatus.NOT_ACCEPTABLE)
.build();
return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus());
}
@ExceptionHandler(RollbackException.class)
public ResponseEntity<ApiErrorsListResponse> handleNotValidException(RollbackException ex){
String errMessage = ex.getCause().getMessage();
List<String> listErrMessage = getListErrMessage(errMessage);
ApiErrorsListResponse response = ApiErrorsListResponse.newBuilder()
.status(HttpStatus.NOT_ACCEPTABLE)
.errorMessage(listErrMessage)
.build();
return new ResponseEntity<>(response, HttpStatus.NOT_ACCEPTABLE);
}
public static List<String> getListErrMessage(String msg){
Stream<String> stream = Arrays.stream(msg.split("\n"))
.filter(s -> s.contains("\t"))
.map(s -> s.replaceAll("^([^\\{]+)\\{", ""))
.map(s -> s.replaceAll("[\"]", ""))
.map(s -> s.replaceAll("=", ":"))
.map(s -> s.replaceAll("interpolatedMessage", "message"))
.map(s -> s.replaceAll("\\{|\\}(, *)?", ""));
return stream.collect(Collectors.toList());
}
public class ApiErrorsListResponse {
private HttpStatus status;
private List<String> errorMessage;
public ApiErrorsListResponse() {
}
...
}
I would double check you've imported the right ConstraintViolationException
The one you want is from the org.hibernate.exception.ConstraintViolationException
package. If you've imported the javax.validation.ConstraintViolationException
it will be skipped as you've experienced.
import org.hibernate.exception.ConstraintViolationException;
@RestController
public class FeatureToggleController {
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex, WebRequest request) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}
This will be called as expected.