Icon for gfsd IntelliJ IDEA

What is the difference between @Controller vs @RestController in Spring Boot?

Learn the basics of @Controller and @RestController in Spring Boot

This post covers the use of controllers in Spring Boot and describes the differences between the @Controller and @RestController annotations.

Table of contents:

Spring Boot is a popular extension to the Spring framework. If you are building web applications using Spring or Spring Boot, you have likely bumped into the notion of Controllers and RestControllers, which are two distinct concepts in the Spring framework. It is also a common question in job interviews to test your knowledge of Spring Boot.

Let’s take a look at the the differences between @Controller and @RestController in the context of Spring Boot. In the following sections, we also provide examples of testing individual controller actions.

What are controllers in Spring Boot?

Per Spring’s MVC (Model-View-Controller) design pattern, controllers are used to handle HTTP requests and to return HTTP responses to the client. In the context of Spring, a controller is a class responsible for preparing a model map.

Besides the data to be displayed, controllers also choose the right view to display said data. To handle controllers in a Spring Boot application, you’ll use either the @Controller or the @RestController annotation.

🌱 Dive deeper into the details of Spring

Looking to get a deeper understanding of the Spring framework? Read our post that explains the differences between Spring vs Spring Boot vs Spring Web MVC vs Spring WebFlux.

And here’s our guide to mocking in Spring Boot.

Differences between @Controller vs @RestController in Spring Boot

There is a difference in how these two handle client requests, which also determines in what situation you should use each one:

  • @Controller : used to declare common web controllers that can return HTTP responses
  • @RestController: used to create controllers for REST APIs that can return JSON responses.

If you’re looking to handle requests from a human client, you’ll want to generate a final HTML view and therefore will use @Controller. If your client is a computer, you’ll most likely want to return an XML or a JSON instead of HTML, so you’ll use @RestController.

Let’s dive into the details of both annotations:

Spring's class hierarchy | Source: https://docs.symflower.com/docs/test-templates/spring-boot-test-template-examples/
Image source: https://docs.symflower.com/docs/test-templates/spring-boot-test-template-examples/

How to use the @Controller annotation in Spring Boot?

@Controller is a long-time Spring annotation that has always been used to define an entrance point for a Spring web application.

In the context of Spring applications, handling incoming requests happens via a handler method in a class annotated with @Controller. Using the @Controller annotation helps Spring auto-detect annotated classes through classpath scanning. Note that @Controller is only used on classes.

@Controller
public class BookController {
   ...

   @GetMapping("/books/most-popular")
   public String getMostPopularBookByISBN() {...}
}

@Controller is best used in UI-based applications where it’s likely that you’ll want to return a view (e.g. an HTML page).

In case you don’t need view resolution and rendering via an HTML template (e.g. if your client is not a user, as is the case with most REST API calls), you’ll need a solution that writes directly to the response body. That’s what @ResponseBody was for in earlier versions of Spring Boot.

Before Spring 4.0 (when @RestController was introduced), if you wanted to handle a REST request with @Controller, you had to combine it with @ResponseBody. Supported at the class as well as the method levels, @ResponseBody is used to indicate that the return value should be bound to the web response body and no view resolver is needed. Using the @ResponseBody annotation with @Controller, a controller can also write directly into the response stream to complete the request.

In versions Spring 4.0 and lower, here’s how you would have used this solution:

@Controller
@ResponseBody
public class MVCControllerWithResponseBody {
   ...
}

Since handling responses for REST in the form of a JSON or XML (without rendering HTML) is a common use case, Spring developers came up with a solution: @RestController.

How to use the @RestController annotation in Spring Boot?

Introduced in Spring 4.0, @RestController is a specialized @Controller annotation. @RestController is best used when you want to return data (e.g. JSON or XML) rather than a view (HTML). @RestController is basically a meta-annotation that combines @Controller and @ResponseBody. Therefore, you don’t have to use both annotations together: the following two snippets of code will achieve the same thing in Spring Boot:

@Controller
@ResponseBody
public class MVCControllerWithResponseBody {
   ...
}
@RestController
public class  MVCControllerWithResponseBody {
  ...
}

One neat thing about @ResponseBody (and therefore, @RestController) is that it supports reactive types, so you can return Reactor or RxJava types along with their asynchronous values.

Controller testing in Spring Boot

There are three ways to test controllers in a Spring Boot environment. You can choose to use:

  • @MockMvc alone: Doesn’t load a Spring context which could be a good thing if your goal is to save resources. But this also limits the usability of standalone @MockMvc to inside-server tests. This option needs more manual setup since you’ll be setting up @MockMVC manually and will explicitly configure the controller under test.
  • @MockMvc + @WebMvcTest: Needs less setup because this option includes a partial Spring controller context (only the web layer is loaded). No manual configuration is needed as you can use annotations like @Autowired or @MockBean.
  • @SpringBootTest: Loads the entire Spring context. This is a heavyweight approach that leads to “fat” tests. Overall, it’s ideal for integration testing rather than unit testing.

Due to the above limitations, in the following, we’ll only cover the option of using the combination of @MockMvc and @WebMvcTest. For a deeper dive into testing with Spring Boot, check out our blog post:

Example: Using @MockMvc with @WebMvcTest to test Spring Boot controllers

Our production code for this example is a simple Book controller that has a get mapping for returning the ISBN of the most popular book:

@Controller
public class BookController {
   ...

   @GetMapping("/books/most-popular")
   public String getMostPopularBookByISBN() {...}
}

In this example, we’ll be using Symflower to generate tests for this application. Symflower generates the following test template that can be readily used to refine the expected behavior of getMostPopularBookByISBN:

@RunWith(SpringRunner.class)
@WebMvcTest(BookController.class)
public class BookControllerTest {
   @Autowired
   private MockMvc mockMvc;

   ...

   @Test
   public void getMostPopularBookByISBN() throws Exception {
       this.mockMvc.perform(get("/books/most-popular"))
           .andExpect(status().isOk())
           .andExpect(view().name(""))
           .andExpect(content().string(""));
   }
}

Next, let’s take a look at a put request which might have the following signature:

@PutMapping("/update-book")
public void updateBook(Book book) {
}

The test generated by Symflower:

@Test
public void updateBook() throws Exception {
   this.mockMvc.perform(put("/books/update-book")
           .param("id", "<value>")
           .param("isbn", "<value>")
           .param("author", "<value>")
           .param("title", "<value>")
           .param("yearPublished", "<value>"))
       .andExpect(status().isOk())
       .andExpect(content().string(""));
}

After this, all you need to do is provide meaningful values within the controller test. Using Symflower, we have significantly reduced the effort it takes to write tests for Spring Boot controllers using @MockMvc with @WebMvcTest. In an evaluation, we used Symflower to generate test templates for 10 open-source Spring Boot applications. See our results in that post.

Here’s a demonstration of generating integration tests for Spring Boot in a refactoring exercise:

Take a look at our documentation for more examples of what types of tests Symflower can generate for you.

Make sure you never miss any of our upcoming content by signing up for our newsletter and by following us on X, LinkedIn, and Facebook.

Try Symflower in your IDE to generate unit test templates & test suites
| 2024-03-16