Exception Handler with Spring Boot

In this article I show how to create an Exception Handler with Spring Boot, creating an aspect and a custom exception in Java.

Content:

  • Custom Exception
  • Runtime Exception
  • Aspect Oriented Programming
  • Custom HTTP Code

For more details, watch this video.

You can find all the code of the article in the following repository.

Custom Exception

The article of today is centered in handling properly the exceptions. The exceptions shouldn’t be feared. The exceptions are part of the application and must be included in the daily work and not in the nightmares. And i’m gonna show how to create my own exception and use it, throw it at the correct moment.

First of all, why do i need to create my own exception? In all kind of applications, errors may occur. We are not perfect. But some of them can be controlled. You have errors like: the user sends wrong data. And there are errors like: “I don’t know, but this is not working”.

For the first one, when the users send incorrect data, I may have some if-else all over the application to detect that. Another way is removing those if-else, clean all of this and throw an exception. As I know the user is sending incorrect data, data that can’t be handled, I can stop running the application. I can return him an error message. That’s a quick way to exit the running application. I mean, the application won’t stop running but the user will see
a response at this moment. Its request just to stop running. I don’t have to care about the returned value. The return value will be the exception itself.

Runtime Exception

Let’s create an exception and see it by examples. There are two types of exceptions: the common exception and the runtime exception. The difference is that the common exception must always be catch, surrounded by a try-catch or declared in the method. And the runtime exception is more silent, I don’t need to surround it with a try-catch.

A common exception is the IOException when reading a file. The compiler is always asking to surround the getInputStream with a try-catch. And a runtime exception is the NullPointerException.

So, if I want to create my own exception, the best option is the runtime exception. Because this way, I control the exception, I control where the exception will be thrown.

public class AppException extends RuntimeException {

    public AppException(String message) {
        super(message);
    }
}

i will just use the message for the moment, but I will add more fields later. And let’s use this exception where there is a problem in the application, when the user requests some incorrect data like when he’s sending an incorrect password, searching for a non-existent user, or creating an already existing user.

    private User getUser(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new AppException("User not found"));
    }

Here, the user is searching for a non-existing user. So, I will throw my own exception.

Ok, but this doesn’t change from before. Let’s see what returns the application when my exception is thrown.

> curl localhost:8080/users/987
{"timestamp": 1595277722360, "status": 500, "error": "Internal Server Error", "message": "User not found"}

I don’t want this to be returned. I don’t want the end users to see that. I don’t even know if the frontend guys know to print that pretty. For that, I will need an aspect, an advice, a controller advice, a class that will surround the controllers, an aspect, which will surround the controllers.

Aspect Oriented Programming

@ControllerAdvice
public class RestExceptionHandler {

    @ExceptionHandler(value = { AppException.class })
    @ResponseBody
    public ResponseEntity<ErrorDto> handleException(AppException ex) {
        return ResponseEntity
                .body(ErrorDto.builder().message(ex.getMessage()).build());
    }
}

This way, when a controller receives an AppException, it will map it into an ErrorDTO instead of continuing with the normal workflow. As now I control the output, I return the DTO instead of letting the raw exception. Let’s see it.

> curl localhost:8080/users/987
{"message": "User not found"}

Better now. But let’s go to the next level. Let’s use the HTTP code next with each AppException.

Custom HTTP Code

import org.springframework.http.HttpStatus;

public class AppException extends RuntimeException {

    private final HttpStatus status;

    public AppException(String message, HttpStatus status) {
        super(message);
        this.status = status;
    }

    public HttpStatus getStatus() {
        return status;
    }
}

And modify to advide to return the correct HTTP code for each case.

@ControllerAdvice
public class RestExceptionHandler {

    @ExceptionHandler(value = { AppException.class })
    @ResponseBody
    public ResponseEntity<ErrorDto> handleException(AppException ex) {
        return ResponseEntity
                .status(ex.getStatus())
                .body(ErrorDto.builder().message(ex.getMessage()).build());
    }
}
private User getUser(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new AppException("User not found", HttpStatus.NOT_FOUND));
    }

Let’s try again now.

This time I will add three V’s at the end to show more verbose information.

> curl localhost:8080/users/987 -vvv
> GET /users/987 HTTP/2
> Host: localhost
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/2 <strong>404</strong>
< date: Mon, 13 Sep 2021 19:51:22 GMT
< content-type: application/json
< content-length: 30
< access-control-allow-origin: *
<
{"message": "User not found"}

The same error message but now with the HTTP code.

Conclusion

  • I’ve created a custom runtime exception, runtime because I don’t want to add the try-catch everywhere, I want the exception to be thrown to the user’s face;
  • I’ve added a field HTTP status to customize the HTTP code to return;
  • I’ve created the exception handler, the aspect, to handle the custom exception and return a beautiful json. This way, I return a custom message, not an ugly stacktrace, with a custom HTTP code. And I can even add more fields, like the input parameters or more debug information. I can even hide the error with an error code, like error 42 when the password is incorrect. This makes difficult to hack the application in production, because nobody but you knows what a 42 code means.

All the code is available in the following repository.


Never Miss Another Tech Innovation

Concrete insights and actionable resources delivered straight to your inbox to boost your developer career.

My New ebook, Best Practices To Create A Backend With Spring Boot 3, is available now.

Best practices to create a backend with Spring Boot 3

One response to “Exception Handler with Spring Boot”

  1. […] No. An exception is here to indicate an incorrect behavior. In this case, I’ve created a custom exception, AppException, which will be managed differently, using an Aspect. […]

    Like

Leave a comment

Discover more from The Dev World - Sergio Lema

Subscribe now to keep reading and get access to the full archive.

Continue reading