Spring Boot RestTemplate setErrorHandler and Timeout exceptions - spring

I have a use case while using restTemplate where I don't want restTemplate to throw exceptions when the response includes bad http codes like 400, 500, 404 etc, so I am using setErrorHandler as below:
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
protected boolean hasError(HttpStatus statusCode) {
return false;
}});
But will this also eat up any connection/socket/read timeout exceptions, if yes is there a way to avoid that.

In case of an exception processing the HTTP request, an exception of
the type RestClientException will be thrown; this behavior can be
changed by plugging in another ResponseErrorHandler implementation
into the RestTemplate.
Source: https://docs.spring.io/spring/docs/5.0.4.RELEASE/spring-framework-reference/integration.html#rest-resttemplate

Related

Error handling on quarkus mutiny rest client

On my quarkus rest project i have a restclient that uses mutiny:
#Path("/")
#RegisterRestClient(configKey = "my-api")
#RegisterClientHeaders
#RegisterProvider(MyExceptionMapper.class)
public interface MyClient {
#POST
#Path("path")
Uni<MyBean> get(String body);
}
I wanna handle propery non 2XX httpError so i have made my ExceptionMaper
public class MyExceptionMapper implements ResponseExceptionMapper<MyException> {
#Override
public MyException toThrowable(Response response) {
//TODO
return new MyException();
}
}
a bad call on the client shows that MyExceptionMapper handle the response but the exception raises and does not became a failure on my Uni Client response object
Uni<MyBean> bean = myClient.get("") // i do not have a failure in case of 4XX http
.onFailure().invoke(fail -> System.out.println("how can i get here?"));
Am i using mutiny on a rest client in the wrong way?
Thanks
UPDATE
ok i forgot to add the dependency quarkus-rest-client-mutiny, adding this i notice 2 things,
i still pass through Myexceptionmapper
i also produce a Uni.failure, but the exception into the failure is not the custom exception i created into MyExceptionmapper but a RestEasyWebApplicationException
Failure : org.jboss.resteasy.client.exception.ResteasyWebApplicationException: Unknown error, status code 400
at org.jboss.resteasy.client.exception.WebApplicationExceptionWrapper.wrap(WebApplicationExceptionWrapper.java:107)
at org.jboss.resteasy.microprofile.client.DefaultResponseExceptionMapper.toThrowable(DefaultResponseExceptionMapper.java:21)
Does the ExceptionMapper becomes useless in this context?
I think this is a bug in quarkus-rest-client-mutiny. I created an Github issue based on your findings.
It will work as you expect if you switch to quarkus-rest-client-reactive

In ResponseEntityExceptionHandler what is the exact difference between handleExceptionInternal and handleException?

I am implementing all the methods of ResponseEntityExceptionHandler because i don't want Spring to send any standard error responses towards the client. There are two seemingly similar methods that confuse me a bit. Namely handleExceptionInternal and handleException. These are the definitions of both methods according to the official documentation.
handleException(Exception ex, WebRequest request)
Provides handling for standard Spring MVC exceptions.
handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request)
A single place to customize the response body of all exception types.
I find these explanations a bit vague. What can be considered 'standard spring mvc exceptions' for example? And should handleExceptionInternal be considered like a 'default' handler method that is used when none of the other methods can catch the spring exception? Please correct me if i'm wrong.
Thank you
handleException method is a common exception handler for standard spring mvc exceptions. Its main task is it maps these exception to respective status code as per http response code convention, which most likely you are not going to change.
e.g.
HttpRequestMethodNotSupportedException -> 405
HttpMediaTypeNotSupportedException -> 415
NoHandlerFoundException -> 404
All these exceptions are handled in their specific handler methods handle{ExceptionName} so that for some reason, if you want to change the status code (or add response body for detailed info), you can do so by overriding specific handler. All these handlers further delegate to handleExceptionInternal.
One thing you have noticed each handle{ExceptionName} methods pass body as null to handleExceptionInternal. These methods just return the status code with no body which doesn't give you more details about the error.
A common practice is to return a custom error response body with details so that your api consumers know the exact error cause. This is the place you can inject your custom body by creating an Error object. A simple error message would look like.
public class ApiError {
private final int status;
private final int message;
public ApiError(int status, int message) {
this.status = status;
this.message = message;
}
// getters
}
And you can override handleExceptionInternal method as:
#Override
protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
ApiError error = new ApiError(status.value(), ex.getMessage());
return super.handleExceptionInternal(ex, error, headers, status, request);
}
Summary
If handleException wouldn't there, then you need to manually map each exceptions to respective error code. If handleExceptionInternal were missing then to inject error body you would need to override each handle{Exception} methods.
Update
RFC for http status code definition.

Better approach than defining an #ExceptionHandler for Exception.class to handle errors?

In my Spring Boot project I defined some #ExceptionHandler classes in a #ControllerAdvice to handle specific exceptions. These build an application-specific JSON reponse instead of the default provided by Spring Boot. So far this works fine for e.g. the MethodArgumentNotValidException which is thrown in case the request validation fails.
#ExceptionHandler(value = {MethodArgumentNotValidException.class})
public ResponseEntity<ApplicationResponse> handleValidationException(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
log.error("Validation failed: {}", errors);
return buildErrorResponse(ApplicationError.REQUEST_BODY_VALIDATION_FAILED, errors.toString(), HttpStatus.BAD_REQUEST);
}
This approach seems reasonable to me for all exceptions I'm aware of. But what happens if an exception is thrown for which no #ExceptionHandler exists? Then Spring Boot's default will kick in resulting in a JSON response which looks different from my custom one.
My first idea was to add an #ExceptionHandler for the Exception class like this:
#ExceptionHandler(value = {Exception.class})
public ResponseEntity<ApplicationResponse> handleValidationException(Exception ex) {
log.error("An unhandled error occurred: {}", ex.getMessage());
return buildErrorResponse(ApplicationError.GENERIC_ERROR, HttpStatus.INTERNAL_SERVER_ERROR);
}
While this seems to be working I wonder if this approach could have some major drawbacks I'm not aware of. How would you response with a custom JSON structure for any error that might occur?
Spring has several ways to deal with an exception handler as you can see here. Use #ControllerAdvice with several #ExceptionHandler has the following advantages:
Centralized class to deal with not catched exceptions.
Customization about how you want to manage specific ones.
Regarding to include a #ExceptionHandler(value = {Exception.class}) to manage exceptions without an specific handler, a better option is create the next one: #ExceptionHandler(Throwable.class), in that way you will be able to manage all potential problems in your application.
About return a Json when in your application an error happens, you can configure it in the response itself. For example:
private ResponseEntity<ErrorResponseDto> buildErrorResponse(RestApiErrorCode errorCode, List<String> errorMessages, HttpStatus httpStatus) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(APPLICATION_JSON);
ErrorResponseDto error = new ErrorResponseDto(errorCode, errorMessages);
return new ResponseEntity<>(error, headers, httpStatus);
}
You can see the rest of the code here

Can not handle JDBCConnectionException in spring rest with custom exception handler

I use a global exception handler in my spring rest app and I would like to hide jdbc exceptions, but it doesn't work as expected. I shut down the database to force a connection exception and I can see the following exception in the log and I receive the default spring error response, but not the one I defined in the exception handler
java.lang.IllegalStateException: Could not resolve parameter [1] in public org.springframework.http.ResponseEntity<java.lang.Object> ...
throws java.io.IOException: No suitable resolver
Here's the code.
#ControllerAdvice
public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler({JDBCConnectionException.class})
public ResponseEntity<Object> dbError(JDBCConnectionException exception,
HttpHeaders headers,
HttpStatus status,
WebRequest request) throws IOException
{
Map<String,Object> body = new HashMap<>();
body.put("errorId",Long.valueOf(201));
body.put("state",HttpStatus.SERVICE_UNAVAILABLE.value());
body.put("message", "internal failure");
body.put("time", new Date().toString());
return new ResponseEntity<>(body, headers, status);
}
Hope you can help me.
I've found the failure...spring can not resolve these two parameters, for that kind of exception.
HttpHeaders headers,
HttpStatus status
It's obviouse the exception mentioned paramter [1]
java.lang.IllegalStateException: Could not resolve parameter [1] in public org.springframework.http.ResponseEntity<java.lang.Object> ...
throws java.io.IOException: No suitable resolver
I removed these two parameters and the exception handler handles the exception.
This code works now
#ExceptionHandler(JDBCConnectionException.class)
public ResponseEntity<Object> dbError(Exception ex,
WebRequest request)
{
Map<String,Object> body = new HashMap<>();
body.put("errorId",Long.valueOf(201));
body.put("state",HttpStatus.SERVICE_UNAVAILABLE.value());
body.put("message", "internal failure");
body.put("time", new Date().toString());
return new ResponseEntity<Object>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
As the annotation implies #ControllerAdvice is used as an extension on your REST endpoints, these exception handlers will process the exception for the REST API and does not influence how it is logged in the console. It will instead determine how exceptions are reported to your end users and allow you to write concise error messages without leaking information about your program.
If you want to completely catch an exception and not only for the REST API take a look at this blog.
However I would not recommend doing this since this will greatly reduce the information available to you as a developer, this information cannot be seen by end users and therefore the REST API custom exception should provide enough abstraction.
I hope this helps you.

How to manage Feign errors?

We are using Spring-boot with Spring-cloud and Spring-cloud-netflix with Spring-cloud-feign.
We are creating our Gateway application that with the help of Feign will try to communicate with our authentication microservice in order to validate their credentials. Here you can see an example of our Feign authentication client:
#FeignClient(value="auth", configuration = AuthClientConfiguration.class)
public interface AuthClient {
#RequestMapping(method = RequestMethod.GET, value = "/tokens", consumes = MediaType.APPLICATION_JSON_VALUE)
Single<Session> getSession(#RequestHeader("Authorization") String token);
}
The question is, how we can deal with all the exceptions that could be raised by the client? I mean, how we can for example catch that a NetworkException or a TimeoutException has been thrown? We've defined our own ErrorDecoder but it appears that this "kind of listener" only works when the request has arrived and the response returned (in our case from authentication client). So, how we can manage this other exceptions?
Best,
Error decoders are decoding HTTP error responses (500, 404, 401, etc...). Exceptions will bubble up in client calls, so using try/catch should work.
try {
return client.home();
} catch (RuntimeException e) {
e.printStackTrace();
throw e;
}

Resources