I'm trying to override the handleMethodArgumentNotValid method but without success.
The error I'm getting is: "the method handleMethodArgumentNotValid(MethodArgumentNotValidException, HttpHeaders, HttpStatus, WebRequest) of type CustomResponseEntityExceptionHandler must override or implement a supertype but I don't know why I'm not doing it already.
Here's what I'm trying:
#ControllerAdvice
#RestController
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
var exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<Object>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
return new ResponseEntity<Object>("test", HttpStatus.NOT_ACCEPTABLE);
}
#ExceptionHandler(UserNotFoundException.class)
public final ResponseEntity<Object> handleUserNotFoundExceptions(Exception ex, WebRequest request) {
var exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<Object>(exceptionResponse, HttpStatus.NOT_FOUND);
}
}
I'm following this tutorial if it's any help: https://www.baeldung.com/global-error-handler-in-a-spring-rest-api
Well if you are not using other parameters try this
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
return new ResponseEntity<Object>("test", HttpStatus.NOT_ACCEPTABLE);
}
Related
I have a problem to return the ErrorDTO from Generic Exception.
When I send a request to http://localhost:8080/v1/locations with this response body shown below, I get this issue underneath GlobalExceptionHandler.
{
"code" : "DELHI_IN_QWERTYUI", -> IT thows an error because of its length (length is 12)
"city_name" : "New Delhi",
"region_name" : "Delhi",
"country_name" : "India",
"country_code" : "IN",
"enabled" : true
}
Here is the method defined in controller shown below?
#PostMapping
public ResponseEntity<Location> addLocation(#RequestBody #Valid Location location) {
Location addedLocation = service.add(location);
URI uri = URI.create("/v1/locations/" + addedLocation.getCode());
return ResponseEntity.created(uri).body(addedLocation);
}
How can I fix the issue?
Here is the ErrorDTO below.
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class ErrorDTO {
private LocalDateTime timestamp;
private int status;
private String path;
private HttpStatus httpStatus;
List<String> errorDetails;
}
Here is the GlobalExceptionError shown below
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
// handleMissingServletRequestParameter : triggers when there are missing parameters
#Override
protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
List<String> details = new ArrayList<String>();
StringBuilder builder = new StringBuilder();
builder.append(ex.getMessage() + " is not supported");
details.add(builder.toString());
HttpStatus httpStatus = HttpStatus.valueOf(status.value());
ErrorDTO error = new ErrorDTO.ErrorDTOBuilder()
.timestamp(LocalDateTime.now())
.status(status.value())
.errorDetails(details)
.path(request.getContextPath())
.httpStatus(httpStatus)
.build();
LOGGER.error("GlobalExceptionHandler | handleHttpRequestMethodNotSupported", error);
return ResponseEntity.status(status).body(error);
}
// handleMethodArgumentNotValid : triggers when #Valid fails
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(err -> errors.put(err.getField(), err.getDefaultMessage()));
LOGGER.error("GlobalExceptionHandler | handleMethodArgumentNotValid", errors);
return ResponseEntity.badRequest()
.body(errors);
}
// handleMissingServletRequestParameter : triggers when there are missing parameters
#Override
protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
List<String> details = new ArrayList<String>();
StringBuilder builder = new StringBuilder();
builder.append(ex.getParameterName());
details.add(builder.toString());
HttpStatus httpStatus = HttpStatus.valueOf(status.value());
ErrorDTO error = new ErrorDTO.ErrorDTOBuilder()
.timestamp(LocalDateTime.now())
.status(status.value())
.errorDetails(details)
.path(request.getContextPath())
.httpStatus(httpStatus)
.build();
LOGGER.error("GlobalExceptionHandler | handleMissingServletRequestParameter", error);
return ResponseEntity.status(status).body(error);
}
// handleHttpMessageNotReadable : triggers when the JSON is malformed
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
List<String> details = new ArrayList<String>();
StringBuilder builder = new StringBuilder();
builder.append(ex.getMessage());
details.add(builder.toString());
HttpStatus httpStatus = HttpStatus.valueOf(status.value());
ErrorDTO error = new ErrorDTO.ErrorDTOBuilder()
.timestamp(LocalDateTime.now())
.status(status.value())
.errorDetails(details)
.path(request.getContextPath())
.httpStatus(httpStatus)
.build();
LOGGER.error("GlobalExceptionHandler | handleHttpMessageNotReadable", error);
return ResponseEntity.status(status).body(error);
}
#ExceptionHandler(Exception.class)
public ErrorDTO handleGenericException(HttpServletRequest request, Exception ex) {
List<String> details = new ArrayList<String>();
details.add(ex.getMessage());
ErrorDTO error = new ErrorDTO.ErrorDTOBuilder()
.timestamp(LocalDateTime.now())
.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
.errorDetails(details)
.path(request.getServletPath())
.httpStatus(HttpStatus.INTERNAL_SERVER_ERROR)
.build();
LOGGER.error("GlobalExceptionHandler | handleGenericException", error);
return error;
}
}
Here is the error shown below.
{
"timestamp": "2023-02-17T12:56:02.341+00:00",
"status": 404,
"error": "Not Found",
"trace": "org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]\r\n\tat org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:273)\r\n\tat org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)\r\n\tat org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:566)\r\n\tat org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743)\r\n\tat org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711)\r\n\tat org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:654)\r\n\tat org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:407)\r\n\tat org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)\r\n\tat org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)\r\n\tat org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)\r\n\tat org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703)\r\n\tat com.skyapi.weatherforecast.location.LocationService$$SpringCGLIB$$0.add(<generated>)\r\n\tat com.skyapi.weatherforecast.location.LocationApiController.addLocation(LocationApiController.java:21)\r\n\tat java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)\r\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:578)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:207)\r\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:152)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:973)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1011)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914)\r\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:731)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885)\r\n\tat jakarta.servlet.http.HttpServlet.service(HttpServlet.java:814)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:223)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:859)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1734)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)\r\n\tat org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.base/java.lang.Thread.run(Thread.java:1589)\r\nCaused by: org.hibernate.exception.DataException: could not execute statement\r\n\tat org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:53)\r\n\tat org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:56)\r\n\tat org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109)\r\n\tat org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95)\r\n\tat org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:200)\r\n\tat org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3429)\r\n\tat org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:4058)\r\n\tat org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:103)\r\n\tat org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:612)\r\n\tat org.hibernate.engine.spi.ActionQueue.lambda$executeActions$1(ActionQueue.java:483)\r\n\tat java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:729)\r\n\tat org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:480)\r\n\tat org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:329)\r\n\tat org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)\r\n\tat org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:107)\r\n\tat org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1425)\r\n\tat org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:477)\r\n\tat org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:2234)\r\n\tat org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:1930)\r\n\tat org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:439)\r\n\tat org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:183)\r\n\tat org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:281)\r\n\tat org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101)\r\n\tat org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:562)\r\n\t... 58 more\r\nCaused by: com.mysql.cj.jdbc.exceptions.MysqlDataTruncation: Data truncation: Data too long for column 'code' at row 1\r\n\tat com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:104)\r\n\tat com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:916)\r\n\tat com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1061)\r\n\tat com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1009)\r\n\tat com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1320)\r\n\tat com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:994)\r\n\tat com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)\r\n\tat com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeUpdate(HikariProxyPreparedStatement.java)\r\n\tat org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:197)\r\n\t... 77 more\r\n",
"message": "could not execute statement; SQL [n/a]",
"path": "/v1/locations"
}
Here is the console output shown
org.springframework.web.HttpMediaTypeNotAcceptableException: No acceptable representation
It return this kind of exception message rather than ErrorDTO.
Here is the repo : Link
How can I fix the issue?
After adding #Data annotation to ErrorDTO, the issue disappeared.
#Builder
#AllArgsConstructor
#NoArgsConstructor
#Data
public class ErrorDTO {
private LocalDateTime timestamp;
private int status;
private String path;
private HttpStatus httpStatus;
List<String> errorDetails;
}
I have requirements that if the JSON for the post request is invalid, I will need to send 400 HTTP response codes and if any fields are not parsable, the return status code will be 422. An example post request can be:
{
"amount": "12.3343",
"timestamp": "2018-07-17T09:59:51.312Z"
}
The Dto class is provided as below,
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
public class TransactionDto {
#NotNull
#Min(0)
private BigDecimal amount;
#NotNull
private LocalDateTime timestamp;
}
This is the controller with POST request,
#Slf4j
#RestController
#RequestMapping("/")
#Validated
public class TransactionController {
#Autowired
private TransactionService transactionService;
#Operation(description = "create a transaction using the provided JSON data")
#ApiResponses(value = {
#ApiResponse(responseCode = "201", description = "Create transaction", content = {
#Content(mediaType = "application/json", schema = #Schema(implementation = Transaction.class))}),
#ApiResponse(responseCode = "204", description = "Transaction is older than 60 seconds", content = #Content(mediaType = "application/json")),
#ApiResponse(responseCode = "500", description = MessageConstant.INTERNAL_SERVER_ERROR_MSG, content = #Content)})
#PostMapping(value = "/transactions")
public ResponseEntity<Object> createProperty(#RequestBody #Valid TransactionDto transactionDto) {
try {
final LocalDateTime transactionTimestamp = transactionDto.getTimestamp();
final LocalDateTime localDateTimeNow = LocalDateTime.now(ZoneOffset.UTC);
final long secondsDuration = Duration.between(transactionTimestamp, localDateTimeNow).toSeconds();
final boolean isFuture = transactionTimestamp.isAfter(localDateTimeNow);
if (isFuture) {
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.UNPROCESSABLE_ENTITY,
"Transaction is in future"), new HttpHeaders(), HttpStatus.ACCEPTED);
}
if (secondsDuration > 60) {
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.NO_CONTENT,
"Transaction is older than 60 seconds"), new HttpHeaders(), HttpStatus.NO_CONTENT);
}
final Transaction transaction = transactionService.createTransaction(transactionDto);
return new ResponseEntity<>(transaction, new HttpHeaders(), HttpStatus.CREATED);
} catch (Exception ex) {
log.error(MessageConstant.INTERNAL_SERVER_ERROR_MSG + ex.getMessage());
return new ResponseEntity<>(ApiResponseMessage.getInternalServerError(), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
If the "amount" is, say, "sfdfd", this is not BigDecimal, we should provide the 422. But if the "amount" is "-12.3343", this is a constraint validation error but the data is valid and parsable. So we can't have the 422.
This is my exception handling class,
#Slf4j
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#Override
#Nonnull
protected ResponseEntity<Object> handleMissingServletRequestParameter(
MissingServletRequestParameterException ex,
#Nonnull HttpHeaders headers,
#Nonnull HttpStatus status,
#Nonnull WebRequest request
) {
String error = ex.getParameterName() + " parameter is missing";
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.BAD_REQUEST,
error), new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
#Override
#Nonnull
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(
#Nonnull HttpMediaTypeNotSupportedException ex,
#Nonnull HttpHeaders headers,
#Nonnull HttpStatus status,
#Nonnull WebRequest request
) {
String message = prepareMessageFromException(ex, (ServletWebRequest) request);
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.UNSUPPORTED_MEDIA_TYPE,
" media type is not supported. Supported media types "), new HttpHeaders(), HttpStatus.UNSUPPORTED_MEDIA_TYPE);
}
#Override
#NonNull
protected ResponseEntity<Object> handleMethodArgumentNotValid(
#NonNull MethodArgumentNotValidException ex,
#NonNull HttpHeaders headers,
#NonNull HttpStatus status,
#NonNull WebRequest request
) {
String message = prepareMessageFromException(ex, (ServletWebRequest) request);
ApiErrorResponse apiError = new ApiErrorResponse(BAD_REQUEST);
apiError.setMessage("Validation error");
apiError.addValidationErrors(ex.getBindingResult().getFieldErrors());
apiError.addValidationError(ex.getBindingResult().getGlobalErrors());
return buildResponseEntity(apiError);
}
#ExceptionHandler(ConstraintViolationException.class)
protected ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex) {
ApiErrorResponse apiError = new ApiErrorResponse(BAD_REQUEST);
apiError.setMessage("Validation error");
apiError.addValidationErrors(ex.getConstraintViolations());
return buildResponseEntity(apiError);
}
#ExceptionHandler(Exception.class)
#ResponseBody
public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
ServletWebRequest req = (ServletWebRequest) request;
String message = prepareMessageFromException(ex, (ServletWebRequest) request);
log.info("{} to {}", req.getHttpMethod(), req.getRequest().getServletPath());
log.error(message, ex);
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.BAD_REQUEST,
message), new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
#Override
#Nonnull
protected ResponseEntity<Object> handleHttpMessageNotReadable(
#Nonnull HttpMessageNotReadableException ex,
#Nonnull HttpHeaders headers,
#Nonnull HttpStatus status,
#Nonnull WebRequest request
) {
String message = prepareMessageFromException(ex, (ServletWebRequest) request);
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, BAD_REQUEST,
message), new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
private String prepareMessageFromException(Exception ex, ServletWebRequest request) {
String message = ex.getMessage();
log.info("{} to {}", request.getHttpMethod(), request.getRequest().getServletPath());
log.error(message);
if (message != null && !message.isEmpty()) {
message = message.split(":")[0];
}
return message;
}
#Override
#Nonnull
protected ResponseEntity<Object> handleHttpMessageNotWritable(
#Nonnull HttpMessageNotWritableException ex,
#Nonnull HttpHeaders headers,
#Nonnull HttpStatus status,
#Nonnull WebRequest request
) {
String error = "Error writing JSON output";
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.INTERNAL_SERVER_ERROR,
"Internal server error. please contact support !!"), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
#Override
#Nonnull
protected ResponseEntity<Object> handleNoHandlerFoundException(
NoHandlerFoundException ex,
#Nonnull HttpHeaders headers,
#Nonnull HttpStatus status,
#Nonnull WebRequest request
) {
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.BAD_REQUEST,
String.format("Could not find the %s method for URL %s", ex.getHttpMethod(), ex.getRequestURL())), new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
private ResponseEntity<Object> buildResponseEntity(ApiErrorResponse apiError) {
return new ResponseEntity<>(apiError, apiError.getStatus());
}
#ExceptionHandler(EntityNotFoundException.class)
protected ResponseEntity<Object> handleEntityNotFound(EntityNotFoundException ex) {
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.NOT_FOUND,
"Resource not found: "), new HttpHeaders(), HttpStatus.NOT_FOUND);
}
#ExceptionHandler(DataIntegrityViolationException.class)
protected ResponseEntity<Object> handleDataIntegrityViolation(DataIntegrityViolationException ex, WebRequest request) {
if (ex.getCause() instanceof ConstraintViolationException) {
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.CONFLICT,
"Database error"), new HttpHeaders(), HttpStatus.CONFLICT);
}
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.INTERNAL_SERVER_ERROR,
"Internal server error. please contact support !!" + ex.getMessage()), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(MethodArgumentTypeMismatchException.class)
protected ResponseEntity<Object> handleMethodArgumentTypeMismatch(MethodArgumentTypeMismatchException ex, WebRequest request) {
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.BAD_REQUEST,
String.format("The parameter '%s' of value '%s' could not be converted to type '%s'", ex.getName(), ex.getValue(), ex.getRequiredType())), new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
}
At the moment, I get 400 in both of the cases mentioned. How do I refactor the code to get the correct response code?
I manage to achieve that after some modification of the existing code,
#Override
#Nonnull
protected ResponseEntity<Object> handleHttpMessageNotReadable(
#Nonnull HttpMessageNotReadableException ex,
#Nonnull HttpHeaders headers,
#Nonnull HttpStatus status,
#Nonnull WebRequest request
) {
String message = prepareMessageFromException(ex, (ServletWebRequest) request);
final Throwable throwableCause = ex.getCause();
if (throwableCause instanceof InvalidFormatException) {
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.UNPROCESSABLE_ENTITY,
message), new HttpHeaders(), HttpStatus.UNPROCESSABLE_ENTITY);
}
return new ResponseEntity<>(ApiResponseMessage.getGenericApiResponse(Boolean.FALSE, HttpStatus.BAD_REQUEST,
message), new HttpHeaders(), HttpStatus.BAD_REQUEST);
}
So when the throwable is InvalidFormatException, I will return the 422 response status code and other the method works as before.
I can't seem to be able to make the following code work.
For some reason neither of the following advice is being thrown to the controller.
#ControllerAdvice
public class ApiExceptionHandler extends ResponseEntityExceptionHandler {
#Autowired
private MessageSource messageSource;
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
String mensagemUsuario = messageSource.getMessage("mensagem.invalida", null, LocaleContextHolder.getLocale());
String mensagemDesenvolvedor = ex.getCause().toString();
List<Erro> erros = Arrays.asList(new Erro(mensagemUsuario, mensagemDesenvolvedor));
return handleExceptionInternal(ex, erros, headers, HttpStatus.BAD_REQUEST, request);
}
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
List<Erro> erros = criarListaDeErros(ex.getBindingResult());
return handleExceptionInternal(ex, erros, headers, HttpStatus.BAD_REQUEST, request);
}
private List<Erro> criarListaDeErros(BindingResult bindingResult) {
List<Erro> erros = new ArrayList<>();
for (FieldError fieldError : bindingResult.getFieldErrors()) {
String mensagemUsuario = messageSource.getMessage(fieldError, LocaleContextHolder.getLocale());
String mensagemDesenvolvedor = fieldError.toString();
erros.add(new Erro(mensagemUsuario, mensagemDesenvolvedor));
}
return erros;
}
#org.springframework.web.bind.annotation.ExceptionHandler({EmptyResultDataAccessException.class})
#ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity<Object> handleEmptyResultDataAccessException(RuntimeException ex, WebRequest request){
String mensagemUsuario = messageSource.getMessage("mensagem.invalida", null, LocaleContextHolder.getLocale());
String mensagemDesenvolvedor = ex.getCause().toString();
List<Erro> erros = Arrays.asList(new Erro(mensagemUsuario, mensagemDesenvolvedor));
return handleExceptionInternal(ex, erros, new HttpHeaders(), HttpStatus.NOT_FOUND, request);
}
#org.springframework.web.bind.annotation.ExceptionHandler({DataIntegrityViolationException.class})
public ResponseEntity<Object> handleDataIntegrityViolationException(DataIntegrityViolationException ex, WebRequest webRequest) {
String mensagemUsuario = messageSource.getMessage("mensagem.invalida", null, LocaleContextHolder.getLocale());
String mensagemDesenvolvedor = ex.getCause().toString();
List<Erro> erros = Arrays.asList(new Erro(mensagemUsuario, mensagemDesenvolvedor));
return handleExceptionInternal(ex, erros, new HttpHeaders(), HttpStatus.BAD_REQUEST, webRequest);
}
public static class Erro {
private String mensagemUsuario;
private String mensagemDesenvolvedor;
public Erro(String mensagemUsuario, String mensagemDesenvolvedor) {
this.mensagemUsuario = mensagemUsuario;
this.mensagemDesenvolvedor = mensagemDesenvolvedor;
}
} }
Project Structure :
It's my first time creating controller advice this way so would very much appreciate if someone could point me what I'm doing wrong.
Thank you.
I have added customexception class to handle my api class.
Exception class
#SuppressWarnings({ "unchecked", "rawtypes" })
#ControllerAdvice
public class ApiExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllExceptions(Exception ex, WebRequest request) {
List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
ErrorMessage error = new ErrorMessage("Server Error", details);
return new ResponseEntity(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(RecordNotFoundException.class)
public final ResponseEntity<Object> handleConfigNotFoundException(RecordNotFoundException ex, WebRequest request) {
List<String> details = new ArrayList<>();
details.add(ex.getLocalizedMessage());
ErrorMessage error = new ErrorMessage("Record Not Found", details);
return new ResponseEntity(error, HttpStatus.NOT_FOUND);
}
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
List<String> details = new ArrayList<>();
for (ObjectError error : ex.getBindingResult().getAllErrors()) {
details.add(error.getDefaultMessage());
}
ErrorMessage error = new ErrorMessage("Validation Failed", details);
return new ResponseEntity(error, HttpStatus.BAD_REQUEST);
}
}
API method
#RequestMapping(value = "/config", params = { "appCode", "appVersion" }, method = RequestMethod.GET)
public ResponseEntity<ConfigResponse> getConfig(#RequestParam(value = "appCode", required = true) String appCode,
#RequestParam(value = "appVersion", required = true) String appVersion) {
List<AppConfig> result = configRepository.findByCodeAndVersion(appCode, appVersion);
if (result.isEmpty()) {
throw new RecordNotFoundException(appCode + " or " + appVersion + "does not exist");
}
AppConfig config = new AppConfig();
return new ResponseEntity<ConfigResponse>(config.convertToResponse(result), HttpStatus.OK);
}
Record Not found
#ResponseStatus(HttpStatus.NOT_FOUND)
public class RecordNotFoundException extends RuntimeException {
public RecordNotFoundException(String exception) {
super(exception);
}
}
I am checking for the record not found but it still gives me the spring exception and not my custom exception.
I am a newbie with spring and spring boot. After creating a simple REST API to perform CRUD operations on Users i have also created custom exception handlers to catch any exceptions occurred in the application. Here is the code which i have written
The controller class
public class UserController {
#Autowired
private IUserService userService;
#ApiOperation(value = "View list of all users", response = Iterable.class)
#RequestMapping(value = "/users", method = RequestMethod.GET)
public #ResponseBody List<User> getAll() throws EntityNotFoundException {
return userService.query();
}
#ApiOperation(value = "View a specific user", response = User.class)
#RequestMapping(value = "/users/{id}", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE })
public #ResponseBody List<User> getUser(#PathVariable(value = "id") String userid) throws EntityNotFoundException {
return userService.query(userid);
}
#ApiOperation(value = "create a user")
#RequestMapping(value = "/users", method = RequestMethod.POST, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<HttpStatus> createUser(#Valid #RequestBody User user) throws Exception {
userService.add(user);
return ResponseEntity.ok(HttpStatus.OK);
}
#ApiOperation(value = "update a user")
#RequestMapping(value = "/users", method = RequestMethod.PUT, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<User> updateUser(#Valid #RequestBody User entity) throws Exception {
User user = userService.update(entity);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
#ApiOperation(value = "delete a user")
#RequestMapping(value = "/userid/{id}", method = RequestMethod.DELETE, produces = {
MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<HttpStatus> deleteUser(#PathVariable(value = "id") String userid) throws Exception {
String result = userService.remove(userid);
if (result.equals(null)) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(HttpStatus.OK);
}
}
Exceptions handlers annotated class with #Controlleradvice
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
/**
* Handles javax.validation.ConstraintViolationException. Thrown
* when #Validated fails.
*
* #param ex
* the ConstraintViolationException
* #return the ApiError object
*/
#ExceptionHandler(javax.validation.ConstraintViolationException.class)
protected ResponseEntity<Object> handleConstraintViolation(javax.validation.ConstraintViolationException ex) {
ApiError apiError = new ApiError(BAD_REQUEST);
apiError.setMessage("Validation error");
return buildResponseEntity(apiError);
}
/**
* Handles EntityNotFoundException. Created to encapsulate errors with more
* detail than javax.persistence.EntityNotFoundException.
*
* #param ex
* the EntityNotFoundException
* #return the ApiError object
*/
#ExceptionHandler(EntityNotFoundException.class)
protected ResponseEntity<Object> handleEntityNotFound(EntityNotFoundException ex) {
ApiError apiError = new ApiError(NOT_FOUND);
apiError.setMessage(ex.getMessage());
return buildResponseEntity(apiError);
}
/**
* Handle Exception, handle generic Exception.class
*
* #param ex
* the Exception
* #return the ApiError object
*/
#ExceptionHandler(MethodArgumentTypeMismatchException.class)
protected ResponseEntity<Object> handleMethodArgumentTypeMismatch(MethodArgumentTypeMismatchException ex,
WebRequest request) {
ApiError apiError = new ApiError(BAD_REQUEST);
apiError.setMessage(String.format("The parameter '%s' of value '%s' could not be converted to type '%s'",
ex.getName(), ex.getValue(), ex.getRequiredType().getSimpleName()));
apiError.setDebugMessage(ex.getMessage());
return buildResponseEntity(apiError);
}
private ResponseEntity<Object> buildResponseEntity(ApiError apiError) {
return new ResponseEntity<>(apiError, apiError.getStatus());
}
}
But, when any exception occurs the application is redirected to /error page rather than being handled by the exception handler class. Can anyone advice on the right way of doing this.
Which exceptions are not been catch by the handler? Are the ConstraintViolationExceptions?
When you do:
#Valid #RequestBody User entity
You are firing the validation in the method, so if the constraints you use to decorate your User class are not fulfilled a MethodArgumentNotValidException will be thrown, not a ConstraintViolationException.
As you are extending ResponseEntityExceptionHandler you should check the code of such class which defines the following method:
#ExceptionHandler({
HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
HttpMediaTypeNotAcceptableException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
ServletRequestBindingException.class,
ConversionNotSupportedException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
MissingServletRequestPartException.class,
BindException.class,
NoHandlerFoundException.class,
AsyncRequestTimeoutException.class
})
#Nullable
public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) throws Exception {
HttpHeaders headers = new HttpHeaders();
if (ex instanceof HttpRequestMethodNotSupportedException) {
HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof HttpMediaTypeNotSupportedException) {
HttpStatus status = HttpStatus.UNSUPPORTED_MEDIA_TYPE;
return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof HttpMediaTypeNotAcceptableException) {
HttpStatus status = HttpStatus.NOT_ACCEPTABLE;
return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, headers, status, request);
}
else if (ex instanceof MissingPathVariableException) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleMissingPathVariable((MissingPathVariableException) ex, headers, status, request);
}
else if (ex instanceof MissingServletRequestParameterException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, headers, status, request);
}
else if (ex instanceof ServletRequestBindingException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleServletRequestBindingException((ServletRequestBindingException) ex, headers, status, request);
}
else if (ex instanceof ConversionNotSupportedException) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleConversionNotSupported((ConversionNotSupportedException) ex, headers, status, request);
}
else if (ex instanceof TypeMismatchException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleTypeMismatch((TypeMismatchException) ex, headers, status, request);
}
else if (ex instanceof HttpMessageNotReadableException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, headers, status, request);
}
else if (ex instanceof HttpMessageNotWritableException) {
HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, headers, status, request);
}
else if (ex instanceof MethodArgumentNotValidException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMethodArgumentNotValid((MethodArgumentNotValidException) ex, headers, status, request);
}
else if (ex instanceof MissingServletRequestPartException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleMissingServletRequestPart((MissingServletRequestPartException) ex, headers, status, request);
}
else if (ex instanceof BindException) {
HttpStatus status = HttpStatus.BAD_REQUEST;
return handleBindException((BindException) ex, headers, status, request);
}
else if (ex instanceof NoHandlerFoundException) {
HttpStatus status = HttpStatus.NOT_FOUND;
return handleNoHandlerFoundException((NoHandlerFoundException) ex, headers, status, request);
}
else if (ex instanceof AsyncRequestTimeoutException) {
HttpStatus status = HttpStatus.SERVICE_UNAVAILABLE;
return handleAsyncRequestTimeoutException((AsyncRequestTimeoutException) ex, headers, status, request);
}
else {
// Unknown exception, typically a wrapper with a common MVC exception as cause
// (since #ExceptionHandler type declarations also match first-level causes):
// We only deal with top-level MVC exceptions here, so let's rethrow the given
// exception for further processing through the HandlerExceptionResolver chain.
throw ex;
}
}
So the MethodArgumentNotValidException is been catch here and the handling of such exception is pass to the handleMethodArgumentNotValid method.
The default handleMethodArgumentNotValid just call:
return handleExceptionInternal(ex, null, headers, status, request);
The better option here is to override handleMethodArgumentNotValid in RestExceptionHandler