Issue
I know it is possible to define custom exception handlers in Spring Boot and have e.g. the IllegalArgumentException
exception mapped to the HTTP 400 Bad Request. I am wondering are there any existing exceptions defined in Spring Web/Boot mapped to the standard HTTP status codes? So that I just can throw them and they will be automatically mapped to the standard HTTP status codes.
Solution
Effectively, ResponseEntityExceptionHandler will, by default, transform Spring internally thrown exceptions to an HTTP status code. However, converting the exception to an HTTP status code does not provide any significant logs about the exception. Good security practices dictate that externally dispatched error message shall be the least informative possible about the internals of a system. Conversely logs shall be as informative as could be.
Moreover, the ResponseEntityExceptionHandler only handle Spring generated exceptions. All business related exceptions must be handled separately. For instance, a "Record not found" exception thrown from a findSomething(xxx) method is not handled by this class.
Following are examples on how to address these shortcomings:
Spring threw internal errors You must override the handler of the exception(s) of interest and provide both an internal log message and an external error message to be returned to the caller.
The @ControllerAdvice is an annotation that wraps @Component classes with classes declaring @ExceptionHandler annotated methods. Simply put, these handlers will wrap all @Component methods.
@Slf4j
@ControllerAdvice
public class InternalExceptionHandler extends ResponseEntityExceptionHandler {
@Override
public ResponseEntity<Object> handleMissingServletRequestParameter(
MissingServletRequestParameterException e,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
LogError error = new LogError("MissingServletRequestParameterException",
HttpStatus.BAD_REQUEST,
String.format("Missing '%s' parameter", e.getParameterName()));
log.debug(error.toJson());
HttpErrorResponse response = new HttpErrorResponse(error.getStatus(), e.getMessage());
return new ResponseEntity<>(response.toJson(),
HeaderFactory.getErrorHeaders(),
response.getStatus());
}
....
}
Business layer thrown errors
You must first create a specific RuntimeException class for each of these exceptions and annotated it woth @ResponseStatus.
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Record not found") //
public class RecordNotFoundException extends RuntimeException {
private static final long serialVersionUID = 8857378116992711720L;
public RecordNotFoundException() {
super();
}
public RecordNotFoundException(String message) {
super(message);
}
}
Then, you create an @ControllerAdvice annotated class that will hold all these exceptions handler method. There are no class to derive from as the internal redirection to these @ExceptionHandler annotated methods are managed by Spring.
@Slf4j
@ControllerAdvice
public class ClientExceptionHandler {
@ExceptionHandler(value = RecordNotFoundException.class)
public ResponseEntity<String> handleRecordNotFoundException(
RecordNotFoundException e,
WebRequest request) {
LogError logging = new LogError("RecordNotFoundException",
HttpStatus.NOT_FOUND,
request.getDescription(true));
log.info(logging.toJson());
HttpErrorResponse response = new HttpErrorResponse(logging.getStatus(), e.getMessage());
return new ResponseEntity<>(response.toJson(),
HeaderFactory.getErrorHeaders(),
response.getStatus());
}
....
}
Finally, the helper classes LogError and HttpErrorResponse are simple formatters for their respective destination.
Hope this helps.
Jake
Answered By - softjake
Answer Checked By - Gilberto Lyons (JavaFixing Admin)