DEV Community

realNameHidden
realNameHidden

Posted on

What Is the Difference Between @ControllerAdvice and @RestControllerAdvice?

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.

  • One is a traditional web application that shows HTML pages in the browser.
  • The other is a REST API used by a mobile app or 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 @ResponseBody if 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
Enter fullscreen mode Exit fullscreen mode

👉 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.

Create spring starter project with spring-starter-web and thymeleaf dependencies


Example 1: End-to-End MVC Flow Using @ControllerAdvice

Step 1: Custom Exception

public class PageNotFoundException extends RuntimeException {
    public PageNotFoundException(String message) {
        super(message);
    }
}
Enter fullscreen mode Exit fullscreen mode

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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"; // error.html
    }
}
Enter fullscreen mode Exit fullscreen mode

application.properties

spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
server.error.whitelabel.enabled=false

Enter fullscreen mode Exit fullscreen mode

error.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Error Page</title>

    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f8f9fa;
            text-align: center;
            padding-top: 80px;
        }
        .error-box {
            background-color: #ffffff;
            padding: 30px;
            width: 450px;
            margin: auto;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
        }
        h1 {
            color: #dc3545;
        }
        p {
            font-size: 18px;
            color: #555;
        }
        a {
            text-decoration: none;
            color: #007bff;
            font-weight: bold;
        }
    </style>
</head>
<body>

<div class="error-box">
    <h1>Oops! Something went wrong</h1>

    <!-- Message sent from @ControllerAdvice -->
    <p th:text="${errorMessage}">
        An unexpected error occurred. Please try again later.
    </p>

    <br/>
    <a href="/pages/1">Go Back to Home</a>
</div>

</body>
</html>

Enter fullscreen mode Exit fullscreen mode

home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home Page</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f6f8;
            text-align: center;
            margin-top: 80px;
        }
        h1 {
            color: #2c3e50;
        }
        p {
            font-size: 18px;
            color: #555;
        }
    </style>
</head>
<body>

<h1>Welcome to Spring MVC Home Page</h1>

<p>
    This page is rendered using <strong>@Controller</strong>  
    and handled by <strong>@ControllerAdvice</strong>.
</p>

<p>
    If an error occurs, you will be redirected to a custom error page.
</p>

</body>
</html>

Enter fullscreen mode Exit fullscreen mode

ThymeleafConfig

@Configuration
@EnableWebMvc
public class ThymeleafConfig {
}

Enter fullscreen mode Exit fullscreen mode

📌 End result:

  • Browser receives an HTML error page
  • Centralized error handling
  • 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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Error Response Model

import java.time.LocalDateTime;

public record ErrorResponse(
        int status,
        String message,
        LocalDateTime timestamp
) {}
Enter fullscreen mode Exit fullscreen mode

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";
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Global Exception Handler Using @RestControllerAdvice

@RestControllerAdvice
public class GlobalRestExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleResourceNotFound(
            ResourceNotFoundException ex) {

        ErrorResponse response = new ErrorResponse(
                HttpStatus.NOT_FOUND.value(),
                ex.getMessage(),
                LocalDateTime.now()
        );

        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
    }
}
Enter fullscreen mode Exit fullscreen mode

📌 End result (JSON response):

{
  "status": 404,
  "message": "User not found with id: 2",
  "timestamp": "2025-01-10T12:30:45"
}
Enter fullscreen mode Exit fullscreen mode

Best Practices

  1. Use @RestControllerAdvice for REST APIs
    This avoids missing @ResponseBody.

  2. Use @ControllerAdvice for MVC apps only
    Especially when returning views or templates.

  3. Create consistent error response structures
    Helps frontend teams and API consumers.

  4. Map correct HTTP status codes
    Use 400, 404, 401, 500 appropriately.

  5. Avoid generic Exception handling first
    Always handle specific exceptions before Exception.class.


Common Mistakes to Avoid

❌ Using @ControllerAdvice in REST APIs without @ResponseBody
❌ Returning HTML error pages from REST APIs
❌ Handling exceptions inside controllers
❌ Exposing stack traces in responses


Conclusion

The difference between @ControllerAdvice and @RestControllerAdvice lies in how your application communicates errors.

  • Use @ControllerAdvice when building traditional web applications that return HTML.
  • Use @RestControllerAdvice when building REST APIs that return JSON.

Both provide centralized, clean, and scalable exception handling. Once you understand this distinction, your Spring Boot applications will be easier to maintain and more professional—both in real projects and interviews.


Top comments (0)