This post covers the use of controllers in Spring Boot and describes the differences between the @Controller
and @RestController
annotations.
Table of contents:
- What are controllers in Spring Boot?
- Differences between
@Controller
vs@RestController
in Spring Boot - Controller testing in Spring Boot
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.
Check out all the other parts of our blog post series on Spring Boot:
- Part 1: An introduction to Spring Boot: the basics
- Part 2: Spring Boot testing best practices & guide
- Part 3: Spring framework basics: Spring vs Spring Boot vs Spring Web MVC vs Spring WebFlux
- Part 4: Spring Web MVC vs Spring WebFlux: differences between Spring web frameworks
- Part 5: How to do mocking in Spring Boot? Best practices for using @MockBean vs @Mock
- Part 7: Spring Boot folder structure best practices
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:
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:
👀 Testing tips for Spring Boot
Best practices for testing Spring Boot applications: a complete guide
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.