Can not handle JDBCConnectionException in spring rest with custom exception handler - spring

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.

Related

Spring-Boot:How to get all request body in WebRequest

Is it possible to fetch RequestBody from WebRequest object?
I am handling exceptions from controller advice so would like to handle the request as well to send data back in response. I saw its documentation what I understood is we can fetch query params from that object for GET request. If there is any other approach which I missed can somebody please help.
#NonNull
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(
#Nullable HttpMessageNotReadableException ex,
#Nullable HttpHeaders headers,
HttpStatus status,
#Nullable WebRequest request) {
//handling of input request we got from Spring Controller
}
Try passing InputStream / Reader for access to the request's content. This will be the raw InputStream/Reader as exposed by the Servlet API. and/or response objects (typically from the Servlet API) as per official documentation.
#NonNull
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(
#Nullable HttpMessageNotReadableException ex,
#Nullable HttpHeaders headers,
HttpStatus status,
#Nullable WebRequest request,
InputStream requestContent
) {
//handling of input request we got from Spring Controller
}
When exception occurs in your controller - catch it and extract any and all info you want. Create your own class - Lets say CustomErrorInfo and fill it with all the info you want and throw your own custom exception that will contain your CustomErrorInfo class and the original exception. Than in your controller advice add method that would handle your custom exception and there you will have an access to your CustomErrorInfo class instance thast you filled when exception occurred

How can I configure a Spring Application to Include the thrown Exception as a message in Error 500 (Internal Server Error)?

I have a spring backend application which throws some helpful custom Java exceptions in response to faulty REST API calls, and I have seen tutorials where the thrown exception also shows up as a message value in the returned code 500 internal server error. I'm wondering if there is a way to configure spring such that these thrown exception messages are sent back with the 500 error response.
This can be achieved using this code.
#ControllerAdvice
class GlobalExceptionHandler {
#ExceptionHandler(Exception.class)
ResponseEntity onAnyUnknownException(Exception exception) {
logger.error("Some unknown exception occured", exception);
Map<String, Object> userResponse = new HashMap<>();
userResponse.put("ERROR", "Your custom message");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(userResponse);
}
}

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

Exception handler for REST controller in spring

I want to handle exceptions so the URL information is automatically shown to the client. Is there an easy way to do this?
<bean id="outboundExceptionAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">
<!-- what property to set here? -->
</bean>
You have two choices:
Spring Reference 15.9.1 HandlerExceptionResolver
Spring HandlerExceptionResolvers ease the pain of unexpected
exceptions that occur while your request is handled by a controller
that matched the request. HandlerExceptionResolvers somewhat resemble
the exception mappings you can define in the web application
descriptor web.xml. However, they provide a more flexible way to
handle exceptions. They provide information about which handler was
executing when the exception was thrown. Furthermore, a programmatic
way of handling exceptions gives you more options for responding
appropriately before the request is forwarded to another URL (the same
end result as when you use the servlet specific exception mappings).
The HandlerExceptionResolver has one method, containing everything you need:
HandlerExceptionResolver.resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex)
Or if you need different handlers for different controllers: Spring Reference Chapter 15.9.2 #ExceptionHandler
#ExceptionHandler(IOException.class)
public String handleIOException(IOException ex, HttpServletRequest request) {
return "every thing you asked for: " + request;
}
Short question short answer
I'm doing the following trick:
#ExceptionHandler(Exception.class)
public ModelAndView handleMyException(Exception exception) {
ModelAndView mv = new ModelAndView("redirect:errorMessage?error="+exception.getMessage());
return mv;
}
#RequestMapping(value="/errorMessage", method=RequestMethod.GET)
#Responsebody
public String handleMyExceptionOnRedirect(#RequestParamter("error") String error) {
return error;
}
Works flawless.

Resources