What Is the Difference Between @ControllerAdvice and @RestControllerAdvice?
Source: Dev.to
Learn the difference between @ControllerAdvice and @RestControllerAdvice in Spring Boot with clear explanations and end‑to‑end Java examples.
Introduction
Imagine you are building two applications using Spring Boot.
- Traditional web application – shows HTML pages in the browser.
- REST API – consumed by a mobile app or a frontend framework like React.
Now suppose something goes wrong—maybe a user is not found or an invalid request is sent.
Do you want to handle errors inside every controller? Of course not.
Spring provides a clean way to handle errors globally using @ControllerAdvice and @RestControllerAdvice.
Beginners often ask:
- Are these two annotations the same?
- When should I use which one?
- Why do REST APIs behave differently?
In this blog we’ll explain the difference between @ControllerAdvice and @RestControllerAdvice using clear concepts and end‑to‑end working examples so you can confidently use them in real Spring Boot projects.
Core Concepts
What Is @ControllerAdvice?
@ControllerAdvice is used to handle exceptions globally across controllers. Think of it as a central error handler for your application.
Simple way to think about it
Instead of every controller handling errors on its own, a single manager handles them for everyone.
Key points
- Works with
@Controller(MVC applications) - Typically returns HTML views or
ModelAndView - Requires
@ResponseBodyif you want JSON
Use cases
- Web applications
- Server‑side rendered pages (Thymeleaf, JSP)
- Custom error pages
What Is @RestControllerAdvice?
@RestControllerAdvice is designed specifically for REST APIs. Technically, it is:
@ControllerAdvice + @ResponseBody
Simple way to think about it
Instead of returning an error page, it returns structured JSON data.
Key points
- Works best with
@RestController - Automatically converts responses to JSON
- Ideal for microservices and APIs
Use cases
- REST APIs
- Mobile or frontend‑based applications
- Microservices architecture
Key Difference at a Glance
| Feature | @ControllerAdvice | @RestControllerAdvice |
|---|---|---|
| Application type | MVC / Web apps | REST APIs |
| Default response | HTML / View | JSON |
Needs @ResponseBody | Yes | No |
| Best suited for | UI‑based apps | APIs & microservices |
End‑to‑End Code Examples (Java 21)
Below are complete working examples showing how each annotation is used in a real scenario.
Example 1: End‑to‑End MVC Flow Using @ControllerAdvice
Step 1 – Custom Exception
public class PageNotFoundException extends RuntimeException {
public PageNotFoundException(String message) {
super(message);
}
}
Step 2 – MVC Controller
@Controller
@RequestMapping("/pages")
public class PageController {
@GetMapping("/{id}")
public String getPage(@PathVariable int id) {
if (id != 1) {
throw new PageNotFoundException("Page not found with id: " + id);
}
return "home"; // returns an HTML page
}
}
Step 3 – Global Exception Handler Using @ControllerAdvice
@ControllerAdvice
public class GlobalMvcExceptionHandler {
@ExceptionHandler(PageNotFoundException.class)
public String handlePageNotFound(PageNotFoundException ex, Model model) {
model.addAttribute("errorMessage", ex.getMessage());
return "error"; // resolves to error.html
}
}
Result: Browser receives an HTML error page – centralized error handling with clean controller code.
Example 2: End‑to‑End REST API Flow Using @RestControllerAdvice
Step 1 – Custom Exception
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
Step 2 – Error Response Model
import java.time.LocalDateTime;
public record ErrorResponse(
int status,
String message,
LocalDateTime timestamp
) {}
Step 3 – REST Controller
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public String getUser(@PathVariable Long id) {
if (!id.equals(1L)) {
throw new ResourceNotFoundException("User not found with id: " + id);
}
return "User found";
}
}
Step 4 – Global Exception Handler Using @RestControllerAdvice
@RestControllerAdvice
public class GlobalRestExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity handleResourceNotFound(
ResourceNotFoundException ex) {
ErrorResponse response = new ErrorResponse(
HttpStatus.NOT_FOUND.value(),
ex.getMessage(),
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
}
}
Result (JSON response):
{
"status": 404,
"message": "User not found with id: 2",
"timestamp": "2025-01-10T12:30:45"
}
Best Practices
- Use
@RestControllerAdvicefor REST APIs – guarantees automatic serialization (e.g., JSON) and avoids missing@ResponseBody. - Use
@ControllerAdvicefor MVC apps only – ideal when you need to return views or templates. - Create consistent error‑response structures – helps frontend teams and API consumers handle errors uniformly.
- Map correct HTTP status codes – use
400,404,401,500, etc., appropriately for each error condition. - Avoid generic
Exceptionhandling first – always handle specific exceptions before falling back toException.class.
Common Mistakes to Avoid
- ❌ Using
@ControllerAdvicein REST APIs without@ResponseBody. - ❌ Returning HTML error pages from REST APIs.
- ❌ Handling exceptions inside individual controllers instead of centralizing them.
- ❌ Exposing stack traces or internal details in API responses.
Conclusion
The difference between @ControllerAdvice and @RestControllerAdvice lies in how your application communicates errors:
- Use
@ControllerAdvicefor traditional web applications that return HTML. - Use
@RestControllerAdvicefor REST APIs that return JSON (or other body‑based formats).
Both annotations provide centralized, clean, and scalable exception handling. Understanding this distinction makes your Spring Boot applications easier to maintain and more professional—whether in real projects or during interviews.
