Featured resource
2026 Tech Forecast
2026 Tech Forecast

1,500+ tech insiders, business leaders, and Pluralsight Authors share their predictions on what’s shifting fastest and how to stay ahead.

Download the forecast
  • Lab
    • Libraries: If you want this lab, consider one of these libraries.
    • Core Tech
Labs

Handle Errors in a Quotes API with Spring Boot 4

In this lab, you will gain hands-on experience with handling exceptions in a Spring Boot REST API. You will learn how to effectively manage the response returned to the user and incorporate logging for developer support. Throughout the lab, you will work with annotations such as ResponseStatusException, ExceptionHandler, and RestControllerAdvice. By the end of this hands-on exercise, you will have a comprehensive understanding of exception handling and logging techniques that can be applied to your own applications.

Lab platform
Lab Info
Level
Beginner
Last updated
Apr 15, 2026
Duration
30m

Contact sales

By clicking submit, you agree to our Privacy Policy and Terms of Use, and consent to receive marketing emails from Pluralsight.
Table of Contents
  1. Challenge

    Introduction

    Welcome to Part 3 of the lab series: Handle Errors in a Quotes API with Spring Boot 4.

    This series of labs will guide you through some of the basic concepts for creating a RESTful API Service using Spring Boot with the Spring Web dependency.

    In this lab, you will implement several different ways of handling exceptions that occur within a Spring Boot REST API. This will include handling the response returned to the user and also logging for developer support.

    Effective exception handling is essential to maintain a stable and reliable software application. Handling exceptions requires providing informative feedback to the user and logging the exception for support and troubleshooting purposes. This approach ensures a smooth user experience and helps developers investigate and diagnose the issue, leading to a stable and reliable application.

    The objective of this lab is to modify the way exceptions are handled for the /api/quotes/9 endpoint. The application currently contains only three hardcoded quotes, so a request for quote 9 should result in a 404 Not Found exception.

  2. Challenge

    Starting the REST API Server

    Start the application and test the endpoint

    To observe the changes made to the REST API, you'll start the server.

    In the first Terminal window, run:

    ./gradlew bootRun
    

    As you make changes to the code, return to this Terminal window and stop the application by pressing Ctrl+C. Then restart the server.

    You can press the up arrow to quickly rerun the previous command.

    In the second Terminal send a request to the API to see the current state of the application, run:

    curl -i http://localhost:8888/api/quotes/9
    

    Note: the -i flag will include the response headers. You may need to scroll up to see these.

    Currently, instead of the expected 404 - Not Found response, the endpoint is returning a 500 - Internal Server Error.


    Why the endpoint returns a 500 error

    If you examine the getQuoteByIndex method in QuotesController.java, it directly returns the outcome of the quotes.getQuoteByIndex(index) call.

    In QuotesService.java, the getQuoteByIndex throws a QuoteIndexOutOfBoundsException when the index falls outside the range of the quotes list.

    The reason for the 500 Internal Server Error is that the QuoteIndexOutOfBoundsException is not being handled. Therefore, the endpoint fails, and the client receives an incorrect response.

    In the following steps, you will learn different ways to handle this exception more effectively.

  3. Challenge

    Throwing a `ResponseStatusException`

    One way to handle exceptions with a specific HTTP status code in Spring is through the ResponseStatusException class. This approach is beneficial when you need to send a particular status code along with an exception message.

    To implement this in the getQuoteByIndex() method in QuotesController.java, wrap the method call in a try-catch block.

    Catch the QuoteIndexOutOfBoundsException exception and throw the ResponseStatusException with the appropriate HTTP status and message to be returned to the client, such as:

    @GetMapping("/{index}") 
    public String getQuoteByIndex(@PathVariable int index) {
        try {
            return quotes.getQuoteByIndex(index);
        } catch (QuoteIndexOutOfBoundsException qex) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "ResponseStatusException");
        }
    }
    

    After making this change, restart the server in the first Terminal window.

    Press Ctrl+C to stop the application, then press the up arrow and run ./gradlew bootRun.

    After restarting the server, you can proceed to rerun the curl request in the second Terminal window.

    curl -i http://localhost:8888/api/quotes/9
    

    Now the endpoint is correctly responding with the 404 - Not Found message. ---

    Point of Interest:
    Throwing ResponseStatusException directly from the QuoteService's getQuoteByIndex method might appear as a simpler solution to handle exceptions in a Spring Boot application. However, this class is part of the org.springframework.web.server package, and adding it to the service class would violate the separation of concerns principle. It would push web implementation details into the application layer, tying the service layer to the web layer. This would make it challenging to modify or replace either layer without affecting the other and would also make the code less reusable and harder to test.

    --- Using ResponseStatusException provides a straightforward approach for sending custom error messages to the client. However, this requires the exception to be handled in each method.

    In the following step, you will learn about another technique to handle exceptions across all methods within a class.

  4. Challenge

    Declaring an `ExceptionHandler` in the `@RestController` Class

    Handle exceptions with @ExceptionHandler

    Another way to handle exceptions within a RestController class is by using the @ExceptionHandler annotation. This allows you to define methods in the controller that handle specific exceptions.

    To implement this approach, add the following method to the QuotesController.java file:

    @ExceptionHandler(QuoteIndexOutOfBoundsException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public String handleExceptions(QuoteIndexOutOfBoundsException ex) {
        return "ExceptionHandler";
    }
    

    The @ExceptionHandler annotation specifies which exception this method handles. The @ResponseStatus annotation sets the HTTP response status to 404 - Not Found. This annotation allows the method to return different types of responses. In this case, the simple string ExceptionHandler is returned to verify the correct handler is being used.

    This method is now configured to catch anyQuoteIndexOutOfBoundsException thrown within this class and respond to the client with a more friendly message.

    Test the exception handler

    Restart the server and resend the curl request.

    curl -i http://localhost:8888/api/quotes/9
    

    You will still see the ResponseStatusException. This is because the getQuoteByIndex method was changed to capture the QuoteIndexOutOfBoundsException and throw a ResponseStatusException.

    To test the @ExceptionHandler, you have two options.

    Option 1: Use the test endpoint

    Test the last change by making a request for the /api/quote/exception endpoint:

    curl -i http://localhost:8888/api/quotes/exception
    

    Option 2: Update the controller method

    You could also update the getQuoteByIndex method back to just returning the response from the service:

    @GetMapping("/{index}") 
    public String getQuoteByIndex(@PathVariable int index) {
        return quotes.getQuoteByIndex(index);
    }
    

    After restarting the server, a request to the /api/quotes/9 endpoint will show the new error message. This shows that the @ExceptionHandler is handling all instances of this particular exception:

    curl -i http://localhost:8888/api/quotes/9
    

    In the next step, you'll see how the ExceptionHandler can be declared across the full application.

  5. Challenge

    Declaring a `@RestControllerAdvice` Class

    In the previous approach, the ExceptionHandler method was used to separate the exception handling logic from the endpoint logic. This reduced code duplication and handled the exception for all methods within the class.

    A more advanced approach is to use RestControllerAdvice class, which allows you to handle exceptions globally across multiple controllers.

    With this approach, the exception handling logic is separated from the main controller logic, improving code organization and maintainability. This separation of concerns allows the controller to focus on handling HTTP requests and responses, while the @RestControllerAdvice manages the exception handling logic.

    To get started, open the GlobalExceptionHandler.java file and decorate the class with the @RestControllerAdvice annotation.

    Then declare the handleException method as in the previous step, but return the message RestControllerAdvice.

    The class should now look like:

    @RestControllerAdvice
    public class GlobalExceptionHandler {
        @ExceptionHandler(QuoteIndexOutOfBoundsException.class)
        @ResponseStatus(HttpStatus.NOT_FOUND)
        public String handleExceptions(QuoteIndexOutOfBoundsException ex) {
            return "RestControllerAdvice";
        }
    }
    

    Restart the server and then make the curl request to the api/quotes/exception endpoint.

    curl -i http://localhost:8888/api/quotes/exception
    

    The ExceptionHandler message will still be displayed. This is because the ExceptionHandler declared within the RestController class takes precedence over the one declared in the RestControllerAdvice class.

    For now, comment out the @ExceptionHandler(QuoteIndexOutOfBoundsException.class) annotation in the QuotesController.java file.

    After restarting the server, send a new request to api/quotes/exception.

    curl -i http://localhost:8888/api/quotes/exception
    

    As a result, the RestControllerAdvice message will be returned, indicating that the GlobalExceptionHandler.java file's ExceptionHandler is now handling the exception instead of the one in the QuotesController.java file.

    You have now seen several ways of handling the response sent back to the client when an exception occurs, but that is only part of the job of handling exceptions.

    In the next step you will see how to add logging to your exceptions.

  6. Challenge

    Add Logging for Troubleshooting Support

    When handling exceptions, it's not enough to just provide a helpful response to the client. While that is an important part of the process, it's equally important to log the exception for the support team to troubleshoot any underlying issues that may have caused the exception. Logging the exception details allows the support team to quickly diagnose and fix the problem. The log can also be used for future analysis and improvement of the application.

    SLF4J is a widely used logging abstraction layer that offers a unified interface for various logging frameworks. This allows developers to utilize SLF4J in their code and change the underlying logging framework, such as Logback or Log4j2, without modifying the code itself. The default logging framework for Spring Boot is Logback.

    To implement SLF4J logging in the GlobalExceptionHandler class, you can create a Logger object using the LoggerFactory class from the SLF4J library. Inside the catch block of the handleExceptions() method, you can log the exception using the error() method of the Logger object. This will log the stack trace of the exception along with any other useful information.

    First, add the following import statements to the top of the class:

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    

    Note: Many of the logging framework libraries provide Logger and LoggerFactory classes. Verify that you are importing from the org.slf4j package.

    Then, inside the GlobalExceptionHandler class, create a Logger instance from the LoggerFactory.getLogger() method:

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    

    Note:

    • The logger variable is declared as private static final. This ensures that only one instance of the logger is created per class, which helps to avoid memory leaks and improves performance.
    • The getLogger() method can take either a Class object or a String. In this case, theGlobalExceptionHandler.class is referenced.

    After creating the logger instance, it can be called within any method of the class to log messages with different severities. SLF4J offers different logging levels that can be utilized to categorize log statements based on their severity.

    The available levels, ordered by increasing severity, are trace, debug, info, warn, and error. Whether a specific level is printed or not, depends on the configuration in the application. By default, Spring Boot is configured to print log statements of info severity or higher.

    To demonstrate how to use the logger with different severity levels, you can add the following code snippet just before the return "RestControllerAdvice"; line in the handleExceptions() method:

    logger.error("Error Message");
    logger.warn("Warning Message");
    logger.info("Info Message");
    logger.debug("Debug Message");
    logger.trace("Trace Message");
    

    Restart the server and then make another request to the api/quotes/exception endpoint:

    curl -i http://localhost:8888/api/quotes/exception
    

    You should now see a logs folder containing an app.log file. Opening that file, you should see several error messages similar to the following:

    2023-05-03T15:53:02.780Z ERROR 5394 --- [http-nio-0.0.0.0-8888-exec-1] c.p.quotes.GlobalExceptionHandler        : Error Message
    2023-05-03T15:53:02.780Z  WARN 5394 --- [http-nio-0.0.0.0-8888-exec-1] c.p.quotes.GlobalExceptionHandler        : Warning Message
    2023-05-03T15:53:02.780Z  INFO 5394 --- [http-nio-0.0.0.0-8888-exec-1] c.p.quotes.GlobalExceptionHandler        : Info Message
    

    Notice that only the ERROR, WARN, and INFO messages have been logged.

  7. Challenge

    Next Steps

    Congratulations on completing part 3 of this lab series on Spring!

    Continue working with this lab to see what improvements you can make.

    Consider the following enhancements:

    • Refactor the response type of the ExceptionHandler class to better communicate the nature of the exception to the client.
    • Modify the logging messages to provide more detail to help with troubleshooting.
    • View and modify some logging options available in the application.properties file. Uncomment the different logging properties, restart the server, and then make a request to see how these options change the reported logs.

    The best to you as you continue your learning journey here at Pluralsight.

About the author

Jeff Hopper is a polyglot solution developer with over 20 years of experience across several business domains. He has enjoyed many of those years focusing on the .Net stack.

Real skill practice before real-world application

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Learn by doing

Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.

Follow your guide

All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.

Turn time into mastery

On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.

Get started with Pluralsight