Skip to main content

Spring MVC Controllers

Introduction

Spring MVC Controllers are the heart of Spring's web framework. They act as the traffic director in the Model-View-Controller (MVC) pattern, handling HTTP requests, processing them, and determining what response to send back. If you're building a web application with Spring, understanding controllers is essential.

In this tutorial, we'll explore:

  • What Spring MVC Controllers are and why they're important
  • How to create and configure basic controllers
  • Different ways to handle web requests
  • Working with request parameters, path variables, and request bodies
  • Returning different types of responses
  • Best practices and common patterns

What is a Spring MVC Controller?

A controller in Spring MVC is a Java class that handles web requests. It's annotated with @Controller or @RestController and contains methods (called handler methods) that process specific requests based on URL patterns, HTTP methods, and other criteria.

Key Characteristics of Controllers

  • They receive and process HTTP requests
  • They interact with service layers and models
  • They determine what view should render the response (or return data directly)
  • They handle exceptions that occur during request processing

Creating Your First Controller

Let's create a simple controller that returns a greeting message:

java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloController {

@GetMapping("/hello")
@ResponseBody
public String hello() {
return "Hello, Spring MVC!";
}
}

Breaking down this code:

  • @Controller: This annotation marks the class as a web controller, capable of handling requests
  • @GetMapping("/hello"): Maps HTTP GET requests to /hello URL to this method
  • @ResponseBody: Indicates that the return value should be bound to the web response body
  • The method returns a simple string that will be displayed in the browser

When you run your Spring application and navigate to http://localhost:8080/hello, you'll see "Hello, Spring MVC!" displayed in your browser.

@Controller vs @RestController

Spring provides two primary annotations for creating controllers:

@Controller

The traditional annotation for Spring MVC controllers. When used alone, it expects methods to return view names, which Spring resolves to actual view templates (like JSP, Thymeleaf, etc.).

java
@Controller
public class ViewController {

@GetMapping("/greet")
public String greet(Model model) {
model.addAttribute("message", "Hello from Spring MVC");
return "greeting"; // Returns greeting.html template
}
}

@RestController

This is a specialized version of the controller that combines @Controller and @ResponseBody. Every method in a @RestController automatically returns data directly in the response body, typically as JSON.

java
@RestController
public class ApiController {

@GetMapping("/api/greeting")
public Greeting getGreeting() {
return new Greeting("Hello from the REST API!");
}
}

Request Mapping Methods

Spring provides various annotations to map HTTP requests to controller methods:

AnnotationHTTP Method
@GetMappingGET
@PostMappingPOST
@PutMappingPUT
@DeleteMappingDELETE
@PatchMappingPATCH

These are specialized shortcuts for the more general @RequestMapping annotation.

Example using @RequestMapping:

java
@Controller
public class RequestMappingController {

@RequestMapping(value = "/example", method = RequestMethod.GET)
@ResponseBody
public String exampleGet() {
return "This handles GET requests to /example";
}

@RequestMapping(value = "/example", method = RequestMethod.POST)
@ResponseBody
public String examplePost() {
return "This handles POST requests to /example";
}
}

Handling Request Parameters

Spring MVC makes it easy to access request parameters:

Using @RequestParam

java
@GetMapping("/search")
@ResponseBody
public String search(@RequestParam String query, @RequestParam(defaultValue = "1") int page) {
return "Searching for: " + query + " on page " + page;
}

If you navigate to /search?query=spring&page=2, the output would be:

Searching for: spring on page 2

For optional parameters, you can use:

java
@GetMapping("/profile")
@ResponseBody
public String getProfile(
@RequestParam String username,
@RequestParam(required = false) String section
) {
if (section != null) {
return "Profile for: " + username + ", Section: " + section;
}
return "Full profile for: " + username;
}

Path Variables

For RESTful URLs, path variables are often preferred:

java
@GetMapping("/users/{id}")
@ResponseBody
public String getUserById(@PathVariable Long id) {
return "Fetching user with ID: " + id;
}

If you navigate to /users/42, the output would be:

Fetching user with ID: 42

You can have multiple path variables:

java
@GetMapping("/users/{userId}/posts/{postId}")
@ResponseBody
public String getUserPost(
@PathVariable Long userId,
@PathVariable Long postId
) {
return "Fetching post " + postId + " for user " + userId;
}

Handling Form Submissions

Controllers can easily handle HTML form submissions:

java
@Controller
public class FormController {

// Display the form
@GetMapping("/register")
public String showForm(Model model) {
model.addAttribute("user", new User());
return "registerForm";
}

// Process the form submission
@PostMapping("/register")
public String processRegistration(@ModelAttribute User user, Model model) {
// Process user registration
model.addAttribute("message", "Registration successful for " + user.getUsername());
return "registrationResult";
}
}

Working with JSON Requests and Responses

For RESTful APIs, you'll often work with JSON:

java
@RestController
public class ProductController {

private final ProductService productService;

public ProductController(ProductService productService) {
this.productService = productService;
}

@PostMapping("/products")
public Product createProduct(@RequestBody Product product) {
return productService.save(product);
}

@GetMapping("/products")
public List<Product> getAllProducts() {
return productService.findAll();
}

@GetMapping("/products/{id}")
public ResponseEntity<Product> getProductById(@PathVariable Long id) {
Product product = productService.findById(id);
if (product != null) {
return ResponseEntity.ok(product);
} else {
return ResponseEntity.notFound().build();
}
}
}

Real-world Application: Task Management API

Let's create a more complete example of a task management API:

java
@RestController
@RequestMapping("/api/tasks")
public class TaskController {

private final TaskService taskService;

public TaskController(TaskService taskService) {
this.taskService = taskService;
}

@GetMapping
public List<Task> getAllTasks() {
return taskService.findAll();
}

@GetMapping("/{id}")
public ResponseEntity<Task> getTaskById(@PathVariable Long id) {
return taskService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}

@PostMapping
public ResponseEntity<Task> createTask(@Valid @RequestBody Task task) {
Task savedTask = taskService.save(task);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedTask.getId())
.toUri();
return ResponseEntity.created(location).body(savedTask);
}

@PutMapping("/{id}")
public ResponseEntity<Task> updateTask(
@PathVariable Long id,
@Valid @RequestBody Task task) {
if (!taskService.existsById(id)) {
return ResponseEntity.notFound().build();
}
task.setId(id);
Task updatedTask = taskService.save(task);
return ResponseEntity.ok(updatedTask);
}

@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteTask(@PathVariable Long id) {
if (!taskService.existsById(id)) {
return ResponseEntity.notFound().build();
}
taskService.deleteById(id);
return ResponseEntity.noContent().build();
}

@GetMapping("/status/{status}")
public List<Task> getTasksByStatus(@PathVariable String status) {
return taskService.findByStatus(status);
}
}

Exception Handling in Controllers

Spring provides several ways to handle exceptions in controllers:

Using @ExceptionHandler

java
@RestController
public class ProductController {

// Controller methods...

@ExceptionHandler(ProductNotFoundException.class)
public ResponseEntity<ErrorResponse> handleProductNotFound(
ProductNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
"PRODUCT_NOT_FOUND",
ex.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
}

Using a Global Exception Handler

java
@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(
ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse(
"RESOURCE_NOT_FOUND",
ex.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}

@ExceptionHandler(ValidationException.class)
public ResponseEntity<ErrorResponse> handleValidation(
ValidationException ex) {
ErrorResponse error = new ErrorResponse(
"VALIDATION_ERROR",
ex.getMessage()
);
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
}

Best Practices for Spring MVC Controllers

  1. Keep controllers focused - Controllers should handle HTTP requests and delegate business logic to service classes.

  2. Use proper HTTP status codes - Return appropriate status codes (200 for success, 404 for not found, etc.).

  3. Validate input - Use validation annotations like @Valid to validate request data.

  4. Handle exceptions gracefully - Implement exception handling to provide meaningful error messages.

  5. Use consistent URL patterns - Follow RESTful conventions for your endpoints.

  6. Document your API - Use Swagger/OpenAPI to document your controller endpoints.

  7. Test your controllers - Write unit and integration tests for your controllers.

Summary

Spring MVC Controllers are the entry point for all web requests in a Spring application. They provide a powerful and flexible way to handle HTTP requests, process data, and return responses. We've covered:

  • Creating basic controllers with @Controller and @RestController
  • Handling different HTTP methods with mapping annotations
  • Working with request parameters and path variables
  • Processing form submissions
  • Working with JSON for RESTful APIs
  • Implementing a complete task management API
  • Handling exceptions properly
  • Following best practices

With this knowledge, you should be able to build robust and well-structured web applications using Spring MVC.

Additional Resources and Exercises

Resources

Exercises

  1. Basic Controller: Create a simple controller that displays a welcome message and the current date/time.

  2. Parameter Handling: Create a calculator controller that can perform basic operations (add, subtract, multiply, divide) based on request parameters.

  3. RESTful API: Implement a book management API with endpoints to create, read, update, and delete books.

  4. Exception Handling: Extend your book API to include proper exception handling for scenarios like book not found or invalid input.

  5. Form Handling: Create a contact form controller that validates and processes form submissions, then displays a thank you page.

Happy coding with Spring MVC Controllers!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)