RestApiExceptionHandlerConfiguration.java
package com.tdmconsult.ete.exceptions;
import java.time.LocalDateTime;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
@RestControllerAdvice
@Slf4j
public class RestApiExceptionHandlerConfiguration {
static final String X_WITH_STACKTRACE = "X-With-Stacktrace";
private static final String LOG_EXCEPTION_MESSAGE = "Handling API exception: ";
@ExceptionHandler(Exception.class)
public ResponseEntity<Object> handleAllExceptions(final Exception ex, final WebRequest request) {
final RestApiErrorResponse error = createErrorReturn(ex, request);
return switch (ex) {
case ResourceNotFoundException specificException -> {
log.debug(LOG_EXCEPTION_MESSAGE, specificException);
yield getNotFoundStatusResponse(error);
}
case IllegalAccessException specificException -> {
log.debug(LOG_EXCEPTION_MESSAGE, specificException);
yield getForbiddenStatusResponse(error);
}
case IllegalArgumentException specificException -> {
log.error(LOG_EXCEPTION_MESSAGE, specificException);
yield getNotAcceptableStatusResponse(error);
}
case IllegalStateException specificException -> {
log.error(LOG_EXCEPTION_MESSAGE, specificException);
yield getNotAcceptableStatusResponse(error);
}
case MissingServletRequestParameterException specificException -> {
log.debug(LOG_EXCEPTION_MESSAGE, specificException);
yield getNotAcceptableStatusResponse(error);
}
case DataIntegrityViolationException specificException -> {
log.error(LOG_EXCEPTION_MESSAGE, specificException);
yield getNotAcceptableStatusResponse(error);
}
case MethodArgumentNotValidException specificException -> {
log.debug(LOG_EXCEPTION_MESSAGE, specificException);
yield getNotAcceptableStatusResponse(error);
}
case HttpMessageNotReadableException specificException -> {
log.debug(LOG_EXCEPTION_MESSAGE, specificException);
yield getNotAcceptableStatusResponse(error);
}
default -> {
log.error(LOG_EXCEPTION_MESSAGE, ex);
yield ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(error);
}
};
}
private RestApiErrorResponse createErrorReturn(final Exception ex, final WebRequest request) {
final boolean showStrackTrace = showStackTrace(request);
final List<String> details = ExceptionUtils.getThrowableList(ex).stream()
.map(ExceptionUtils::getMessage)
.toList();
return RestApiErrorResponse.builder()
.message(ex.getMessage())
.type(ex.getClass().getSimpleName())
.details(details)
.stacktrace(showStrackTrace ? ExceptionUtils.getStackTrace(ex) : null)
.timestamp(LocalDateTime.now())
.build();
}
private boolean showStackTrace(final WebRequest request) {
return StringUtils.isNotEmpty(request.getHeader(X_WITH_STACKTRACE))
&& Boolean.parseBoolean(request.getHeader(X_WITH_STACKTRACE));
}
private ResponseEntity<Object> getNotAcceptableStatusResponse(final RestApiErrorResponse error) {
return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE)
.body(error);
}
private ResponseEntity<Object> getForbiddenStatusResponse(final RestApiErrorResponse error) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(error);
}
private ResponseEntity<Object> getNotFoundStatusResponse(final RestApiErrorResponse error) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(error);
}
}