Adam Siemion
Adam Siemion

Reputation: 16029

Spring Boot default exceptions mapped to standard HTTP status codes

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.

Upvotes: 8

Views: 12350

Answers (2)

softjake
softjake

Reputation: 1076

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

Upvotes: 11

Karol Dowbecki
Karol Dowbecki

Reputation: 44932

There is a handful e.g. HttpRequestMethodNotSupportedException which maps to 405.

Take a look at ResponseEntityExceptionHandler.handleException() method which defines basic rules for handling common exceptions in Spring MVC. You will find

NoSuchRequestHandlingMethodException.class,
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

Upvotes: 5

Related Questions