-
Notifications
You must be signed in to change notification settings - Fork 38.6k
Description
Jan Mares opened SPR-16743 and commented
Problem - after calling other service with RestTemplate, the call fails because of deserialization failure, but the exception is not logged
Expected behaviour - stack trace of the exception can be found in the logs
Actual behaviour - only this line appears in the log and the server returns 500
c.d.s.p.c.c.e.BadRequestExceptionHandler : Unknown exception type: org.springframework.web.client.RestClientException
Reason - exception is swallowed in ResponseEntityExceptionHandler (https://github.com/spring-projects/spring-framework/blob/v5.0.5.RELEASE/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandler.java#L188) because not only type of the exception, but also the type of its cause is used to match method in ExceptionHandlerMethodResolver(https://github.com/spring-projects/spring-framework/blob/v5.0.5.RELEASE/spring-web/src/main/java/org/springframework/web/method/annotation/ExceptionHandlerMethodResolver.java#L136)
Code:
@ControllerAdvice
public class BadRequestExceptionHandler extends ResponseEntityExceptionHandler
{
@ExceptionHandler(value = {BadRequestException.class})
protected ResponseEntity<Object> handleBadRequest(BadRequestException ex, WebRequest request)
{
return handleExceptionInternal(ex, ex.getMessage(), new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}
}
The swallowing most probably happens, because exception org.springframework.web.client.RestClientException is thrown with HttpMessageNotReadableException as its cause. When ExceptionHandlerMethodResolver looks for a handler method in resolveMethodByThrowable, first it does not find one, but when it tries a cause of the exception (HttpMessageNotReadableException) it finds one in ResponseEntityExceptionHandler::handleException. That results in this method being called, but the instance passed to the method is not the one of HttpMessageNotReadableException, but of RestClientException and therefore it does not match any of the instanceof ifs in the method and falls in the last else, resulting in logging only that little fragment shown above.
IMHO trying to match against cause as well as the exception itself is dangerous and rather unexpected. As we reuse code, we need to reuse the exceptions that come with it. But in this case an exception of type HttpMessageNotReadableException thrown from contoller trying to deserialize the request is something completely different to the same exception wrapped in RestClientException when trying to parse a response of dependant microservice. Also, why only one level down, why not two levels - cause of a cause, or three cause of a cause of a cause ...
Hacky temporary solution - as we cannot override handleException in ResponseEntityExceptionHandler , we had to override handleInternalException in the following way:
@Override
protected ResponseEntity<Object> handleExceptionInternal(
final Exception ex,
final Object body,
final HttpHeaders headers,
final HttpStatus status,
final WebRequest request
)
{
if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
// due to problem with cause and the fact the handleException in the parent is final
logger.warn("Potentially unlogged exception. Re-logging: ", ex);
}
return super.handleExceptionInternal(ex, body, headers, status, request);
}
which is not very nice.
Affects: 4.3.16, 5.0.5
Issue Links:
- @ExceptionHandler should match cause as well (e.g. for exception thrown from argument formatter) [SPR-14291] #18863
@ExceptionHandler
should match cause as well (e.g. for exception thrown from argument formatter)
Referenced from: commits a200df6, 318d04c, 7b894fe, 193c289, ed44262, f2cc70e
Backported to: 4.3.17