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.javaStep 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.javaStep 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.javaStep 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"
}
Output5. 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 To | Only @RestController classes | Both @Controller and @RestController classes |
Default Response Type | JSON (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 Handling | Global handling for REST APIs | Global 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 Case | Best for pure REST APIs | Suitable 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?
You should use @RestControllerAdvice when you want to handle exceptions globally for REST APIs, ensure consistent JSON/XML error responses, reduce code duplication, handle validation errors, or log exceptions centrally.
Is @RestControllerAdvice mandatory for exception handling in Spring Boot?
No, it is not mandatory. You can use @ExceptionHandler inside individual controllers, but @RestControllerAdvice helps centralize and standardize error handling across multiple controllers.
Can @RestControllerAdvice handle validation errors?
Yes, it can handle validation exceptions like MethodArgumentNotValidException and can be used to return structured validation error messages.
Does @RestControllerAdvice apply to all controllers by default?
Yes, by default, it applies to all @RestController components in the application unless scoped using basePackages, basePackageClasses, or assignableTypes.
What happens if a controller has its own @ExceptionHandler method?
The @ExceptionHandler inside a controller takes precedence over the global @RestControllerAdvice handler for that specific exception type.
How do I log request details when handling exceptions in @RestControllerAdvice?
You can use WebRequest in your exception handler to log request details like the requested URL and parameters.
Can I use @RestControllerAdvice to handle multiple exception types?
Yes, you can define multiple @ExceptionHandler methods inside a single @RestControllerAdvice class to handle different exception types.
Can I define multiple @RestControllerAdvice classes in my project?
Yes, you can have multiple @RestControllerAdvice classes targeting different controllers or exception categories.
How do I test my @RestControllerAdvice exception handlers?
You can use MockMvc in your unit tests to verify that your exception handlers return the expected error responses.
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