@RestController & @Controller
Beginner@RestController combines @Controller and @ResponseBody, turning every handler method into a JSON/XML endpoint without needing explicit serialisation annotations.
Overview
@Controller marks a class as an MVC controller whose methods return view names resolved by a ViewResolver. @RestController is a composed annotation (@Controller + @ResponseBody) that skips view resolution and writes the return value directly to the HTTP response body via HttpMessageConverters. For pure REST APIs you almost always use @RestController. You can still return a ResponseEntity<T> from @RestController methods to control status codes and headers precisely.
@RestController Basics
Every public method in a @RestController is treated as an endpoint. Jackson (included via spring-boot-starter-web) serialises the return value to JSON by default. Returning null yields a 200 with an empty body; use ResponseEntity to return 204 or 404 explicitly.
@RestController
@RequestMapping("/api/products")
public class ProductController {
private final ProductService productService;
public ProductController(ProductService productService) {
this.productService = productService;
}
@GetMapping
public List<ProductDTO> listAll() {
return productService.findAll(); // serialised to JSON array
}
@GetMapping("/{id}")
public ResponseEntity<ProductDTO> getById(@PathVariable Long id) {
return productService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ProductDTO create(@RequestBody @Valid CreateProductRequest req) {
return productService.create(req);
}
}@Controller for MVC Views
@Controller methods return a String view name or a ModelAndView. The DispatcherServlet forwards to the ViewResolver (Thymeleaf, JSP, etc.). You can mix — annotate individual methods with @ResponseBody to return JSON from a @Controller.
@Controller
@RequestMapping("/products")
public class ProductViewController {
@GetMapping
public String list(Model model) {
model.addAttribute("products", productService.findAll());
return "products/list"; // resolves to templates/products/list.html
}
@GetMapping("/{id}/edit")
public String editForm(@PathVariable Long id, Model model) {
model.addAttribute("product", productService.findById(id).orElseThrow());
return "products/edit";
}
// JSON endpoint mixed into a @Controller
@GetMapping("/search")
@ResponseBody
public List<ProductDTO> search(@RequestParam String q) {
return productService.search(q);
}
}Exception Handling with @ControllerAdvice
@RestControllerAdvice (= @ControllerAdvice + @ResponseBody) centralises exception handling across all controllers, avoiding try-catch boilerplate in each endpoint.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(EntityNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFound(EntityNotFoundException ex) {
return new ErrorResponse("NOT_FOUND", ex.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleValidation(MethodArgumentNotValidException ex) {
String msg = ex.getBindingResult().getFieldErrors().stream()
.map(e -> e.getField() + ": " + e.getDefaultMessage())
.collect(Collectors.joining(", "));
return new ErrorResponse("VALIDATION_FAILED", msg);
}
}Key Points to Remember
- 1@RestController = @Controller + @ResponseBody on every method.
- 2Return values are serialised by HttpMessageConverters (Jackson for JSON by default).
- 3@Controller returns view names; add @ResponseBody on individual methods for JSON.
- 4Use ResponseEntity<T> for fine-grained control over status codes and headers.
- 5@RestControllerAdvice centralises error handling across all controllers.
- 6Content negotiation is driven by the Accept header — @RestController supports both JSON and XML with the right dependency.
Interview Questions
Sign in to ask AriaWhat is the difference between @Controller and @RestController?
When would you use @Controller instead of @RestController?
How does @ResponseStatus interact with ResponseEntity?
How does Spring select which HttpMessageConverter to use?
Explain how @RestControllerAdvice works and why it is preferred over try-catch in controllers.
Ask Aria about @RestController & @Controller
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.