Spring Boot’s @RestController vs @Controller: A Comprehensive Guide

Marcelo Domingues
devdomain
Published in
14 min readSep 3, 2024

--

In web application development, particularly with Spring Boot, understanding the distinctions between @Controller and @RestController is crucial. Both annotations play significant roles in handling HTTP requests, but they serve different purposes and are optimized for distinct use cases. In this guide, we will dig into the intricacies of both annotations, exploring their differences, how they work under the hood, and providing practical examples to help you use them effectively.

Reference Image

Introduction to Spring MVC and Controllers

Spring MVC (Model-View-Controller) is a powerful framework designed to simplify web application development by clearly separating concerns:

  • Model: Represents the application data.
  • View: Handles the display logic, often utilizing templating engines like Thymeleaf, JSP, or Freemarker.
  • Controller: Processes user input, interacts with the model and returns a response or a view.

Controllers play a pivotal role in handling incoming HTTP requests, orchestrating business logic, and determining the view or data to return.

Spring Boot, an extension of the Spring framework, enhances this development experience by providing automatic configuration, embedded servers, and easy dependency management. In this context, @Controller and @RestController serve as the core annotations for defining controllers.

Understanding @Controller

The Purpose of @Controller

The @Controller annotation is the standard way to define a controller in a Spring MVC application. It is used for handling traditional web requests where the application needs to return a view (HTML, JSP, etc.) as part of the response. The key functionality of @Controller is its integration with view templates, enabling dynamic page generation.

When a controller class is annotated with @Controller, Spring treats it as a web controller. Methods within this class can handle incoming HTTP requests and return the name of a view. In essence, it’s used when you want to build server-rendered web applications, where views are rendered and sent to the browser.

How @Controller Works

The primary role of @Controller is to facilitate web pages' rendering. It interacts with the view layer by resolving the view names returned by controller methods and serving dynamic content to the user.

For instance, let’s say you’re building a web page that displays a welcome message. You’d use @Controller as follows:

package com.medium.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class WelcomeController {

@GetMapping("/welcome")
public String welcome(Model model) {
model.addAttribute("message", "Welcome to Spring Boot!");
return "welcome"; // The view resolver maps this to "welcome.html" or "welcome.jsp"
}
}

In this example:

  • The method is mapped to the /welcome URL.
  • A Model object is used to pass data to the view.
  • The return value, "welcome", refers to a template (e.g., welcome.html in Thymeleaf) that will be rendered by Spring's view resolver.

View Resolution in @Controller

When a method in a @Controller returns a string, that string is interpreted as a view name. The Spring framework has a ViewResolver that resolves this name to an actual view template, such as a Thymeleaf file (welcome.html) or a JSP page (welcome.jsp).

For example, the return "welcome" line from the previous code snippet would instruct the ViewResolver to look for a file named welcome.html in the src/main/resources/templates directory if you are using Thymeleaf.

Here’s an example of what the project’s root directory structure might look like:

my-spring-boot-app/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── medium/
│ │ │ ├── controller/
│ │ │ │ └── WelcomeController.java
│ │ │ └── api/
│ │ │ └── UserRestController.java
│ │ └── resources/
│ │ ├── static/
│ │ ├── templates/
│ │ │ └── welcome.html
│ │ └── application.properties
│ └── test/
│ └── java/
│ └── com/
│ └── medium/
│ └── MySpringBootAppTests.java
└── pom.xml

In this directory structure:

  • src/main/java/com/medium/controller/WelcomeController.java: This is where your WelcomeController class is located, handling the /welcome endpoint.
  • src/main/resources/templates/welcome.html: This is the Thymeleaf template that corresponds to the return "welcome" statement in the WelcomeController. When this endpoint is accessed, the ViewResolver will map the view name "welcome" to this welcome.html file.

The ViewResolver works with the templates found under the src/main/resources/templates/ directory, which is the standard location for view templates in a Spring Boot project using Thymeleaf.

This structure ensures that when a user accesses the /welcome URL, Spring Boot renders the welcome.html template located in the templates directory, providing a seamless integration between your controller logic and the view layer.

Data Binding with Model

The Model interface is essential when passing data from the controller to the view. You can bind data to the Model object, which will be accessible in the view layer.

Here’s an example of passing data to the view:

package com.medium.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class UserController {

@GetMapping("/user")
public String userProfile(Model model) {
User user = new User("John", "Doe", "john.doe@example.com");
model.addAttribute("user", user);
return "profile";
}
}

The user object is added to the Model, and in the corresponding profile.html view, you can access the user object using templating syntax like Thymeleaf’s ${user.name}.

Common Use Cases for @Controller

  1. Rendering Web Pages: When the client expects HTML, you use @Controller to process the request and return a view.
  2. Form Submissions: Handling form submissions and passing data to the view for display or processing.
  3. Redirecting and Forwarding: @Controller methods can forward requests to other methods or redirect users to different URLs.

In summary, @Controller is well-suited for traditional web applications where server-side rendering of views is required.

Understanding @RestController

The Purpose of @RestController

Introduced in Spring 4.0, @RestController is a specialized version of @Controller designed specifically for building RESTful APIs. It simplifies the development of RESTful web services by eliminating the need to explicitly annotate methods with @ResponseBody. When using @RestController, every method in the class is automatically treated as if it had @ResponseBody, meaning the return values are serialized to JSON or XML and sent directly to the HTTP response body.

This makes @RestController ideal for creating APIs that return data to be consumed by clients like JavaScript applications, mobile apps, or even other backend services.

How @RestController Works

The @RestController annotation in Spring Boot is a specialized version of the @Controller annotation. It is designed for building RESTful web services and simplifies the development process by combining the functionality of @Controller and @ResponseBody.

When you annotate a class with @RestController, you’re telling Spring that this class is a RESTful controller, and therefore, all methods within it should return data directly rather than resolving to a view. Spring Boot automatically serializes the return values of these methods into the appropriate format (usually JSON) and sends them as the HTTP response body.

Let’s break down how @RestController works with an example:

package com.medium.api;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ApiController {

@GetMapping("/api/greeting")
public String greet() {
return "Hello, RESTful World!";
}
}

Explanation:

  • @RestController Annotation: This annotation marks the ApiController class as a RESTful controller. Unlike a traditional @Controller, a @RestController does not resolve views but instead focuses on returning data directly.
  • @GetMapping("/api/greeting"): The @GetMapping annotation maps HTTP GET requests to the greet method. When a client makes a GET request to /api/greeting, this method is invoked.
  • Return Value: The greet method returns a simple string "Hello, RESTful World!". Because the class is annotated with @RestController, Spring automatically processes this return value.
  • Automatic Serialization: Normally, when a controller returns a value, it would be interpreted as a view name. However, because this is a @RestController, Spring Boot instead converts (serializes) the return value to JSON. The String returned by the greet method is wrapped in a JSON object.

The response that the client receives when they hit the /api/greeting endpoint would look like this:

{
"message": "Hello, RESTful World!"
}

Behind the Scenes:

  1. Request Handling: When a client sends an HTTP GET request to /api/greeting, Spring MVC matches this request to the greet method in the ApiController class due to the @GetMapping annotation.
  2. Method Execution: The greet method executes and returns the string "Hello, RESTful World!".
  3. Response Conversion: Since the ApiController is annotated with @RestController, Spring Boot uses an HttpMessageConverter (typically the Jackson library for JSON) to convert the returned string into a JSON object.
  4. HTTP Response: Finally, Spring Boot wraps the string in a JSON format and sends it back to the client as the HTTP response body. The client receives this data, which can be easily consumed by frontend applications, mobile apps, or other services.

Important Notes:

  • JSON Format: If the method returns an object instead of a primitive type or string, Spring Boot will convert the entire object to JSON. For example, if the greet method returned a custom Greeting object, the JSON response would include all fields of that object.
  • Content-Type Header: The response’s Content-Type header is automatically set to application/json, indicating that the data format is JSON.
  • Flexibility: While JSON is the default format, Spring Boot can also serialize responses to other formats like XML, depending on the client’s Accept header or the application configuration.

Here’s an extended example that returns a more complex object:

package com.medium.api;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ApiController {

@GetMapping("/api/greeting")
public Greeting greet() {
return new Greeting("Hello", "RESTful World!");
}
}

class Greeting {
private String salutation;
private String target;
public Greeting(String salutation, String target) {
this.salutation = salutation;
this.target = target;
}
// Getters and Setters
}

The response for this method would be:

{
"salutation": "Hello",
"target": "RESTful World!"
}

In this more complex example, the Greeting object is serialized into a JSON object with salutation and target fields.

Enabling XML Serialization in Spring Boot

To enable XML serialization in a Spring Boot application, you can use the jackson-dataformat-xml dependency, which adds support for XML in Jackson, the default serialization library used by Spring Boot.

Add the Jackson XML Dependency

Add the following dependency to your pom.xml file:

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-xml -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>

This library enables Jackson to handle XML serialization and deserialization.

Configuring XML Serialization

With this dependency in place, Spring Boot can now automatically handle XML serialization. The same controller method can support both JSON and XML, depending on the client’s Accept header.

Here’s an example:

package com.medium.api;

import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

@JacksonXmlRootElement(localName = "User")
public class User {

private String firstName;
private String lastName;
private String email;

public User(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}

// Getters and Setters
}

The @JacksonXmlRootElement annotation is used to specify the root element name in the XML output.

Then, in your controller:

package com.medium.api;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class UserRestController {

@GetMapping(value = "/user", produces = {"application/json", "application/xml"})
public User getUser() {
return new User("John", "Doe", "john.doe@example.com");
}
}

In this example:

  • The getUser method can produce both application/json and application/xml responses.
  • If the client sends an Accept: application/json header, they receive a JSON response.
  • If the client sends an Accept: application/xml header, they receive an XML response.

Testing XML Output

You can test the XML output using tools like curl or Postman by setting the Accept header to application/xml.

Here’s what the XML output might look like:

<User>
<firstName>John</firstName>
<lastName>Doe</lastName>
<email>john.doe@example.com</email>
</User>

Optional: Using JAXB for XML Serialization

Alternatively, if you prefer using JAXB (Java Architecture for XML Binding) for XML serialization, you can include the following dependency in your pom.xml:

<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>

Then, annotate your model class with JAXB annotations:

package com.medium.api;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "User")
public class User {

private String firstName;
private String lastName;
private String email;

public User() {}

public User(String firstName, String lastName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}

@XmlElement
public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

@XmlElement
public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

@XmlElement
public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}
}

This approach uses JAXB annotations like @XmlRootElement and @XmlElement to control the XML output.

With JAXB configured, Spring Boot will use JAXB for XML serialization whenever the client requests XML.

Common Use Cases for @RestController

  1. RESTful APIs: When building a backend service for a front-end client (e.g., Angular, React), @RestController is ideal for returning JSON responses.
  2. Microservices: @RestController is commonly used in microservice architectures where services communicate via HTTP and exchange data in JSON or XML format.
  3. Data-driven Applications: When the primary function of your application is to return data (e.g., an API for fetching user profiles, product information, etc.).

Key Differences Between @Controller and @RestController

Although both @Controller and @RestController are used to handle HTTP requests, they serve different purposes and behave differently:

Purpose:

  • @Controller: Used to return views as part of a traditional web application.
  • @RestController: Used to return data (typically JSON) for RESTful services.

Response Handling:

  • @Controller: Typically returns a view name, which is resolved to a view template like HTML or JSP.
  • @RestController: Automatically serializes the return value into JSON or XML and sends it as the HTTP response body.

Automatic Serialization:

  • @Controller: Requires the @ResponseBody annotation to return JSON or XML data.
  • @RestController: Automatically serializes data, eliminating the need for @ResponseBody.

View Resolution:

  • @Controller: Resolves a view template (like a web page) and renders it.
  • @RestController: Does not resolve views; instead, it returns data directly to the client.

Implementation Examples

In this section, we’ll dive into two practical examples that illustrate the use of @Controller and @RestController in a Spring Boot application. These examples will demonstrate how to build a traditional web application with server-side rendering and a RESTful API that returns JSON data.

Example 1: Using @Controller for Web Pages

In a traditional web application, you may want to render HTML pages that are generated on the server side. This is where @Controller comes into play. It allows you to handle HTTP requests, pass data to the view, and return the name of the view that should be rendered.

Step 1: Define the Controller

Here’s an example of how to create a simple web page using @Controller in Spring Boot:

package com.medium.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {

@GetMapping("/home")
public String home(Model model) {
model.addAttribute("message", "Welcome to the Home Page");
return "home"; // This returns the "home.html" view template
}
}

Explanation:

  • @Controller Annotation: The HomeController class is annotated with @Controller, which tells Spring that this class is a web controller responsible for handling HTTP requests.
  • @GetMapping("/home"): The home method is mapped to the /home URL. When this URL is accessed, the home method is executed.
  • Model Object: The Model object is used to pass data from the controller to the view. In this case, a message is added to the model, which will be accessible in the view template.
  • View Name: The method returns "home", which corresponds to the name of the Thymeleaf template (home.html) that will be rendered. Spring’s ViewResolver will map this view name to an actual HTML file in the src/main/resources/templates/ directory.

Step 2: Create the Thymeleaf Template

Next, you need to create the HTML file that will be rendered when the /home URL is accessed. This file should be placed in the src/main/resources/templates/ directory.

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Home Page</title>
</head>
<body>
<h1 th:text="${message}"></h1>
</body>
</html>

Explanation:

  • Thymeleaf Template: This is a basic Thymeleaf template. Thymeleaf is a popular templating engine for Spring Boot that allows you to dynamically generate HTML content.
  • Dynamic Content: The th:text attribute is used to bind the message variable (passed from the controller) to the content of the <h1> tag. When the page is rendered, the message ("Welcome to the Home Page") will be displayed on the screen.

Project Structure

Here’s what your project structure might look like:

my-spring-boot-app/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── medium/
│ │ │ └── controller/
│ │ │ └── HomeController.java
│ │ └── resources/
│ │ └── templates/
│ │ └── home.html
└── pom.xml

Step 3: Running the Application

Once you’ve defined your controller and created the Thymeleaf template, you can run your Spring Boot application. When you access http://localhost:8080/home in your browser, Spring Boot will render the home.html template with the dynamic message passed from the HomeController.

Result:

You should see a webpage that displays the following content:

Welcome to the Home Page

This example demonstrates how @Controller can be used to build traditional server-rendered web pages in a Spring Boot application.

Example 2: Using @RestController for a REST API

In modern web applications, it’s common to expose RESTful APIs that return data in JSON format. This is where @RestController comes into play. Unlike @Controller, @RestController automatically serializes the returned data into JSON (or XML) and sends it directly as the HTTP response body.

Step 1: Define the REST Controller

Here’s an example of how to create a simple REST API using @RestController:

package com.medium.api;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ApiRestController {

@GetMapping("/api/greeting")
public Greeting greet() {
return new Greeting("Hello", "RESTful World!");
}
}

Explanation:

  • @RestController Annotation: The ApiRestController class is annotated with @RestController, which tells Spring that this class is a RESTful web service. Unlike @Controller, @RestController combines @Controller and @ResponseBody, meaning that all methods return data directly in the HTTP response body.
  • @GetMapping("/api/greeting"): The greet method is mapped to the /api/greeting URL. When this URL is accessed via an HTTP GET request, the greet method is executed.
  • Return Value: The method returns a Greeting object, which is automatically serialized into JSON by Spring Boot.

Here’s the Greeting class:

package com.medium.api;

public class Greeting {

private String salutation;
private String target;

public Greeting(String salutation, String target) {
this.salutation = salutation;
this.target = target;
}

// Getters and Setters
}

Explanation:

  • Greeting Class: This is a simple Java class (POJO) with two fields: salutation and target. This class will be serialized into JSON when it’s returned from the greet method.

Step 2: Running the Application

Once you’ve defined your REST controller and the Greeting class, you can run your Spring Boot application. You can test the API endpoint using tools like Postman, curl, or simply your browser.

curl http://localhost:8080/api/greeting

Result:

When you access the /api/greeting endpoint, the response will be:

{
"salutation": "Hello",
"target": "RESTful World!"
}

Explanation:

  • Automatic Serialization: Spring Boot automatically converts the Greeting object into JSON format. The JSON object contains the fields salutation and target, populated with the values "Hello" and "RESTful World!", respectively.
  • Efficient API Development: This demonstrates how @RestController simplifies the development of RESTful services by handling the conversion of Java objects to JSON (or XML) automatically.

Project Structure

Here’s what your project structure might look like for this example:

my-spring-boot-app/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── medium/
│ │ │ └── api/
│ │ │ ├── ApiRestController.java
│ │ │ └── Greeting.java
└── pom.xml

Summary

These two examples illustrate the fundamental differences between @Controller and @RestController:

  1. @Controller: Ideal for traditional server-rendered web applications, where you return a view name that is resolved to an HTML page. The controller handles HTTP requests and passes data to the view for rendering.
  2. @RestController: Perfect for building RESTful APIs where the primary goal is to return data in a format like JSON. The controller handles HTTP requests and automatically serializes the return value into JSON, making it suitable for consumption by web clients, mobile apps, or other services.

Conclusion

Understanding the difference between @Controller and @RestController is crucial for developing efficient web applications and APIs with Spring Boot. While @Controller is ideal for traditional, server-side rendered web applications, @RestController shines when building RESTful services that return data directly to the client.

By using these annotations appropriately, you can ensure your Spring Boot applications are optimized for their intended use cases, whether rendering dynamic views or serving data to client applications. This knowledge will enable you to make informed decisions when structuring your Spring Boot projects, leading to more maintainable and scalable applications.

Explore More on Spring and Java Development:

Enhance your skills with our selection of articles:

  • Spring Beans Mastery (Dec 17, 2023): Unlock advanced application development techniques. Read More
  • JSON to Java Mapping (Dec 17, 2023): Streamline your data processing. Read More
  • Spring Rest Tools Deep Dive (Nov 15, 2023): Master client-side RESTful integration. Read More
  • Dependency Injection Insights (Nov 14, 2023): Forge better, maintainable code. Read More
  • Spring Security Migration (Sep 9, 2023): Secure your upgrade smoothly. Read More
  • Lambda DSL in Spring Security (Sep 9, 2023): Tighten security with elegance. Read More
  • Spring Framework Upgrade Guide (Sep 6, 2023): Navigate to cutting-edge performance. Read More

--

--

Marcelo Domingues
devdomain

🚀 Senior Software Engineer | Crafting Code & Words | Empowering Tech Enthusiasts ✨ 📲 LinkedIn: https://www.linkedin.com/in/marcelogdomingues/