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:
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.).
@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.
@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:
Annotation | HTTP Method |
---|---|
@GetMapping | GET |
@PostMapping | POST |
@PutMapping | PUT |
@DeleteMapping | DELETE |
@PatchMapping | PATCH |
These are specialized shortcuts for the more general @RequestMapping
annotation.
Example using @RequestMapping
:
@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
@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:
@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:
@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:
@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:
@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:
@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:
@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
@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
@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
-
Keep controllers focused - Controllers should handle HTTP requests and delegate business logic to service classes.
-
Use proper HTTP status codes - Return appropriate status codes (200 for success, 404 for not found, etc.).
-
Validate input - Use validation annotations like
@Valid
to validate request data. -
Handle exceptions gracefully - Implement exception handling to provide meaningful error messages.
-
Use consistent URL patterns - Follow RESTful conventions for your endpoints.
-
Document your API - Use Swagger/OpenAPI to document your controller endpoints.
-
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
-
Basic Controller: Create a simple controller that displays a welcome message and the current date/time.
-
Parameter Handling: Create a calculator controller that can perform basic operations (add, subtract, multiply, divide) based on request parameters.
-
RESTful API: Implement a book management API with endpoints to create, read, update, and delete books.
-
Exception Handling: Extend your book API to include proper exception handling for scenarios like book not found or invalid input.
-
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! :)