Spring Boot RestControllerAdvice Annotation

Spring Boot RestControllerAdvice Annotation: Global Exception Handling Made Easy

Learn how to use RestControllerAdvice annotation in Spring Boot Rest APIs for global exception handling. Understand its benefits and how it is different from @ControllerAdvice.

1. Introduction

When developing REST APIs in Spring Boot, handling exceptions gracefully is crucial for a smooth user experience. Instead of writing repetitive try-catch blocks in every controller, Spring provides @RestControllerAdvice to centralize exception handling.

This guide will cover:

  • What @RestControllerAdvice is.
  • The key differences between @RestControllerAdvice and @ControllerAdvice.
  • The type of exception details available.
  • A step-by-step example demonstrating its usage.

2. What is @RestControllerAdvice?

@RestControllerAdvice is a specialized annotation in Spring that combines @ControllerAdvice (global exception handling) and @ResponseBody (auto-serialization to JSON/XML).

It acts as a global interceptor for exceptions thrown by @RestController classes,  allowing you to handle exceptions globally across all your REST controllers and return structured JSON/XML responses.



3. How Does @RestControllerAdvice Work?

In this section, we’ll explore its working mechanism, how it catches exceptions, processes them using @ExceptionHandler, and ensures consistent JSON/XML responses for REST APIs.

  • Global Exception Handling for REST APIs:
    • @RestControllerAdvice intercepts exceptions thrown by any @RestController in the application.
    • It ensures that error responses are returned in required JSON/XML format instead of HTML.
  • Uses @ExceptionHandler to Handle Specific Exceptions:
    • Within a @RestControllerAdvice class, you can define methods annotated with @ExceptionHandler to handle specific exceptions.
    • These methods process the exception and return a structured error response.
  • Automatically Converts Responses to JSON/XML:
    • Since @RestControllerAdvice combines @ControllerAdvice with @ResponseBody, it serializes exception responses based on the request’s Accept header. If the Accept header is missing, Spring Boot defaults to JSON unless XML is explicitly configured as a priority.
  • Applies to All @RestController Classes Globally:
    • It eliminates the need to define @ExceptionHandler methods inside individual controllers, reducing duplication.
    • This makes exception handling more consistent and manageable across the application.
  • Supports Custom Error Response Structures:
    • You can return custom response objects with error codes, messages, timestamps, and additional details to improve API error handling.
  • Can Target Specific Controllers (Optional):
    • By specifying basePackages or assignableTypes in @RestControllerAdvice, you can restrict its scope to specific controllers.

4. Example: Handling Exceptions with @RestControllerAdvice

Let’s build a simple example where an API throws UserNotFoundException, and we handle it globally using @RestControllerAdvice.

Step 1: Create a Simple REST Controller

Here, we throw a custom exception UserNotFoundException when a user is not found.

import com.bootcamptoprod.exception.UserNotFoundException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class UserController {

    private final Map<Integer, String> users = Map.of(1, "John Doe", 2, "Jane Doe");

    @GetMapping("/users/{id}")
    public String getUserById(@PathVariable int id) {
        if (!users.containsKey(id)) {
            throw new UserNotFoundException("User not found with ID: " + id);
        }
        return users.get(id);
    }

}
UserController.java

Step 2: Create a Custom Exception Class

This exception will be caught and handled globally using @RestControllerAdvice.

public class UserNotFoundException extends RuntimeException {
    public UserNotFoundException(String message) {
        super(message);
    }
}
UserNotFoundException.java

Step 3: Create Error Response POJO

This POJO will be used to throw an exception in custom format with our specified fields.

import java.time.LocalDateTime;

public class ErrorResponse {

    private LocalDateTime timestamp;
    private int status;
    private String errorCode;
    private String message;
    private String path;
    
    // Constructors
    // Getters and setters  
}
ErrorResponse.java

Step 4: Implement @RestControllerAdvice for Global Exception Handling

Exceptions thrown in controllers will be caught and handled centrally using @RestControllerAdvice, ensuring consistent and structured error responses across the application.

import com.bootcamptoprod.exception.UserNotFoundException;
import com.bootcamptoprod.exception.response.ErrorResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;

import java.time.LocalDateTime;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex, WebRequest webRequest) {

        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setTimestamp(LocalDateTime.now());
        errorResponse.setStatus(HttpStatus.NOT_FOUND.value());
        errorResponse.setErrorCode("USER_NOT_FOUND");
        errorResponse.setMessage(ex.getMessage());
        errorResponse.setPath(webRequest.getDescription(false));

        return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND);
    }
}
GlobalExceptionHandler.java


Note: We can also retrieve request information using WebRequest, which can be useful for logging purposes or customizing error responses. In the example above, webRequest.getDescription(false) provides the request path, allowing us to include it in the error response for better debugging and tracking.


Step 6: Verify the Output

When you send an invalid GET request such as http://localhost:8080/users/99 then you will notice the following error response in JSON format returned by our application including

  • timestamp – When the error occurred
  • status – The HTTP status code
  • errorCode – The error code
  • message – Detailed exception message
  • path – URL suffix returning the path for which error response is returned

Output:

{
  "timestamp": "2025-03-08T21:58:39.979439",
  "status": 404,
  "errorCode": "USER_NOT_FOUND",
  "message": "User not found with ID: 99",
  "path": "uri=/users/99"
}
Output


5. Targeting Specific Controllers with @RestControllerAdvice


By default, @RestControllerAdvice applies globally to all @RestController classes in a Spring Boot application. However, you can restrict its scope to specific controllers using the basePackages, basePackageClasses, or assignableTypes attributes. This allows for more granular exception handling, ensuring that only selected controllers are affected.


a. Using basePackages to Target Specific Packages

@RestControllerAdvice(basePackages = "com.example.user")
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex, WebRequest webRequest) {
        // Exception handling logic for UserNotFoundException
    }
}
GlobalExceptionHandler.java
  • This @RestControllerAdvice will only apply to controllers inside the “com.example.user” package.
  • Other controllers in different packages will not be affected by this advice.

b. Using basePackageClasses to Target Controllers in a Specific Package

Instead of specifying package names as strings, you can reference one or more classes. The package of each specified class will be scanned, and exception handling will be applied to all controllers within those packages.

@RestControllerAdvice(basePackageClasses = {UserController.class})
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex, WebRequest webRequest) {
        // Exception handling logic for UserNotFoundException
    }
}
GlobalExceptionHandler.java
  • The exception handler applies to all controllers within the package(s) of the specified class(es).
  • Unlike basePackages, this is not string-based but instead relies on actual class references.
  • It is a type-safe approach, avoiding issues with incorrect package name strings.
  • Multiple classes can be specified, and their respective packages will all be scanned.

c. Using assignableTypes to Target Specific Controller Classes

Instead of specifying entire packages, you can target specific controllers using assignableTypes. This is useful when you want to apply custom exception handling to selected controllers.

@RestControllerAdvice(assignableTypes = {UserController.class, AdminController.class})
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex, WebRequest webRequest) {
        // Exception handling logic for UserNotFoundException
    }
}
GlobalExceptionHandler.java
  • This advice only applies to UserController and AdminController.
  • Other controllers in the project will not use this exception handler.


6. @RestControllerAdvice vs. @ControllerAdvice

Both @RestControllerAdvice and @ControllerAdvice are used for global exception handling, but they serve different purposes:

Feature@RestControllerAdvice@ControllerAdvice
Applies ToOnly @RestController classesBoth @Controller and @RestController classes
Default Response TypeJSON (or XML, if configured)HTML, JSON, or XML (depends on the controller)
Includes @ResponseBody?✅ Yes (by default)❌ No (must be added manually for JSON/XML)
Scope of Exception HandlingGlobal handling for REST APIsGlobal handling for both APIs and web views
Supports View Rendering?❌ No✅ Yes (can return HTML views)
Handles Model Attributes?❌ No✅ Yes (can be used to set attributes for views)
Use CaseBest for pure REST APIsSuitable for applications with both REST APIs and web views

7. Source Code

The complete source code of the above rest controller advice example can be found here.

8. Things to Consider

When implementing @RestControllerAdvice for centralized exception handling in Spring Boot, keep these key considerations in mind:

  • Scope of Exception Handling: If no restrictions are provided, @RestControllerAdvice applies to all @RestController components. You can limit its scope using basePackages, basePackageClasses, or assignableTypes.
  • Overriding Global Exception Handlers: A controller-specific @ExceptionHandler takes precedence over a global exception handler, so be cautious about conflicts.
  • Choosing Between @RestControllerAdvice and @ControllerAdvice: Use @RestControllerAdvice for APIs returning JSON/XML and @ControllerAdvice for both REST and MVC controllers (returning views or HTML pages).
  • Logging Request Information for Debugging: Use WebRequest in exception handlers to log request details like URL and parameters for debugging and better error customization.
  • Returning Consistent Error Responses: Define a standard error response structure (ErrorResponse DTO) with fields like timestamp, status, errorCode, message, and path for uniform API responses.
  • Handling Multiple Exception Types: You can specify multiple exception classes in a single @ExceptionHandler method to avoid redundant code.
  • Returning the Correct HTTP Status Codes: Ensure error responses return appropriate HTTP status codes (404, 400, 500, etc.) instead of the default 200 OK.
  • Avoiding Exception Swallowing: Always log exceptions before handling them to prevent silent failures and debugging difficulties.
  • Testing Your Exception Handling: Use MockMvc to write unit tests and verify that exception handlers return the expected error responses.

9. FAQs

When should I use @RestControllerAdvice?

Is @RestControllerAdvice mandatory for exception handling in Spring Boot?

Can @RestControllerAdvice handle validation errors?

Does @RestControllerAdvice apply to all controllers by default?

What happens if a controller has its own @ExceptionHandler method?

How do I log request details when handling exceptions in @RestControllerAdvice?

Can I use @RestControllerAdvice to handle multiple exception types?

Can I define multiple @RestControllerAdvice classes in my project?

How do I test my @RestControllerAdvice exception handlers?



10. Conclusion

In conclusion, @RestControllerAdvice provides a centralized way to handle exceptions in Spring Boot REST APIs, ensuring consistent and structured error responses. It helps simplify error handling, improve maintainability, and customize responses as needed.

11. Learn More

#

Interested in learning more?

Spring RestTemplate Logging: Print Requests and Responses for Better Insights



Add a Comment

Your email address will not be published.