Skip to main content

Spring MVC RequestMapping

Introduction

In Spring MVC, routing HTTP requests to the appropriate controller methods is a fundamental operation. The @RequestMapping annotation is the cornerstone of this functionality, allowing developers to map web requests to specific handler methods. This annotation is versatile, powerful, and essential for any Spring MVC application.

In this guide, you'll learn how to use @RequestMapping effectively, understand its various attributes, and see real-world examples of its implementation. By the end, you'll be comfortable using this annotation to build robust web applications with Spring MVC.

What is @RequestMapping?

@RequestMapping is an annotation used to map web requests to Spring Controller methods. When a request matches the mapping criteria defined by this annotation, the associated method is executed to handle the request.

The annotation can be applied at two levels:

  • Class level: Maps requests to a specific controller
  • Method level: Maps requests to a specific handler method within a controller

Basic Usage

Class-Level Mapping

java
@Controller
@RequestMapping("/users")
public class UserController {
// All methods in this controller are relative to /users
}

Method-Level Mapping

java
@Controller
@RequestMapping("/users")
public class UserController {

@RequestMapping("/list")
public String listUsers(Model model) {
// This method handles /users/list
return "userList";
}

@RequestMapping("/details")
public String userDetails(Model model, @RequestParam Long id) {
// This method handles /users/details
return "userDetails";
}
}

RequestMapping Attributes

HTTP Methods

By default, @RequestMapping responds to all HTTP methods. To restrict a method to specific HTTP verbs, use the method attribute:

java
@RequestMapping(value = "/create", method = RequestMethod.POST)
public String createUser(@ModelAttribute User user) {
// Handle POST requests to /users/create
return "redirect:/users/list";
}

@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
public String getUser(@PathVariable Long id) {
// Handle GET requests to /users/{id}
return "userDetails";
}

Spring also provides method-specific shortcuts:

java
@GetMapping("/users")
public String listUsers() {
// Equivalent to @RequestMapping(value="/users", method=RequestMethod.GET)
return "userList";
}

@PostMapping("/users")
public String addUser(@ModelAttribute User user) {
// Equivalent to @RequestMapping(value="/users", method=RequestMethod.POST)
return "redirect:/users";
}

@PutMapping("/users/{id}")
public String updateUser(@PathVariable Long id, @ModelAttribute User user) {
// Handles PUT requests to /users/{id}
return "redirect:/users";
}

@DeleteMapping("/users/{id}")
public String deleteUser(@PathVariable Long id) {
// Handles DELETE requests to /users/{id}
return "redirect:/users";
}

Path Variables

Path variables allow you to capture values from the URL path:

java
@GetMapping("/users/{id}")
public String getUser(@PathVariable("id") Long userId, Model model) {
// The {id} part of the URL will be bound to the userId parameter
model.addAttribute("user", userService.findById(userId));
return "userDetails";
}

If the method parameter name matches the path variable name, you can simplify:

java
@GetMapping("/users/{id}")
public String getUser(@PathVariable Long id, Model model) {
// Variable name 'id' matches the path variable {id}
model.addAttribute("user", userService.findById(id));
return "userDetails";
}

You can use multiple path variables in a single URL:

java
@GetMapping("/users/{userId}/posts/{postId}")
public String getUserPost(
@PathVariable Long userId,
@PathVariable Long postId,
Model model
) {
model.addAttribute("user", userService.findById(userId));
model.addAttribute("post", postService.findById(postId));
return "userPost";
}

Request Parameters

To map request parameters to method parameters:

java
@GetMapping("/search")
public String searchUsers(
@RequestParam(name = "query", required = false) String searchQuery,
@RequestParam(defaultValue = "1") int page,
Model model
) {
// Handles requests like /search?query=john&page=2
model.addAttribute("results", userService.search(searchQuery, page));
return "searchResults";
}

Request parameter attributes:

  • name or value: The parameter name (optional if method parameter name matches)
  • required: Whether the parameter is required (default: true)
  • defaultValue: Default value if parameter is missing or empty

Consuming and Producing Media Types

You can restrict handling to specific content types:

java
@PostMapping(
value = "/users",
consumes = "application/json",
produces = "application/json"
)
@ResponseBody
public User createUser(@RequestBody User user) {
// Only processes requests with Content-Type: application/json
// Returns data with Content-Type: application/json
return userService.save(user);
}

You can specify multiple values:

java
@PostMapping(
value = "/users",
consumes = {"application/json", "application/xml"},
produces = {"application/json", "application/xml"}
)
@ResponseBody
public User createUser(@RequestBody User user) {
// Handles both JSON and XML content types
return userService.save(user);
}

Headers and Parameters Conditions

You can restrict mapping based on headers and parameters:

java
@GetMapping(value = "/users", headers = "X-API-VERSION=1")
public String getUsersV1() {
// Only handles requests with header X-API-VERSION: 1
return "usersV1";
}

@GetMapping(value = "/users", params = "version=2")
public String getUsersV2() {
// Only handles requests like /users?version=2
return "usersV2";
}

Practical Examples

RESTful API Controller

Here's an example of a RESTful controller using @RequestMapping:

java
@RestController
@RequestMapping("/api/users")
public class UserApiController {

private final UserService userService;

public UserApiController(UserService userService) {
this.userService = userService;
}

@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}

@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}

@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = userService.save(user);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(savedUser.getId())
.toUri();
return ResponseEntity.created(location).body(savedUser);
}

@PutMapping("/{id}")
public ResponseEntity<User> updateUser(
@PathVariable Long id,
@RequestBody User user
) {
if (!userService.exists(id)) {
return ResponseEntity.notFound().build();
}
user.setId(id);
User updatedUser = userService.update(user);
return ResponseEntity.ok(updatedUser);
}

@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
if (!userService.exists(id)) {
return ResponseEntity.notFound().build();
}
userService.delete(id);
return ResponseEntity.noContent().build();
}
}

Form Handling Controller

Here's an example of form handling with @RequestMapping:

java
@Controller
@RequestMapping("/users")
public class UserFormController {

private final UserService userService;

public UserFormController(UserService userService) {
this.userService = userService;
}

@GetMapping("/list")
public String listUsers(Model model) {
model.addAttribute("users", userService.findAll());
return "users/list";
}

@GetMapping("/new")
public String showCreateForm(Model model) {
model.addAttribute("user", new User());
return "users/form";
}

@PostMapping("/new")
public String createUser(
@Valid @ModelAttribute("user") User user,
BindingResult result
) {
if (result.hasErrors()) {
return "users/form";
}
userService.save(user);
return "redirect:/users/list";
}

@GetMapping("/edit/{id}")
public String showEditForm(@PathVariable Long id, Model model) {
User user = userService.findById(id);
model.addAttribute("user", user);
return "users/form";
}

@PostMapping("/edit/{id}")
public String updateUser(
@PathVariable Long id,
@Valid @ModelAttribute("user") User user,
BindingResult result
) {
if (result.hasErrors()) {
return "users/form";
}
user.setId(id);
userService.update(user);
return "redirect:/users/list";
}
}

File Upload Controller

Here's an example of handling file uploads:

java
@Controller
@RequestMapping("/files")
public class FileUploadController {

private final StorageService storageService;

public FileUploadController(StorageService storageService) {
this.storageService = storageService;
}

@GetMapping("/upload")
public String showUploadForm() {
return "upload";
}

@PostMapping("/upload")
public String handleFileUpload(
@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes
) {
try {
storageService.store(file);
redirectAttributes.addFlashAttribute(
"message",
"Successfully uploaded " + file.getOriginalFilename()
);
} catch (Exception e) {
redirectAttributes.addFlashAttribute(
"error",
"Failed to upload " + file.getOriginalFilename()
);
}
return "redirect:/files/upload";
}

@GetMapping("/download/{filename:.+}")
public ResponseEntity<Resource> downloadFile(@PathVariable String filename) {
Resource file = storageService.loadAsResource(filename);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getFilename() + "\"")
.body(file);
}
}

Advanced Usage

URI Patterns

Spring MVC supports Ant-style path patterns:

  • ? matches one character
  • * matches zero or more characters
  • ** matches zero or more directories in a path
java
@GetMapping("/users/**")
public String handleUserRequests() {
// Matches any path starting with /users/
return "userView";
}

@GetMapping("/files/*.pdf")
public String handlePdfFiles() {
// Matches /files/report.pdf but not /files/docs/report.pdf
return "pdfView";
}

@GetMapping("/reports/??.html")
public String handleReports() {
// Matches /reports/q1.html but not /reports/2022.html
return "reportView";
}

Regular Expressions in Path Patterns

You can use regular expressions for more precise path matching:

java
@GetMapping("/users/{username:[a-z0-9]+}")
public String getUserProfile(@PathVariable String username) {
// Only matches usernames with lowercase letters and numbers
return "userProfile";
}

@GetMapping("/orders/{orderId:[A-Z]{2}\\d{6}}")
public String getOrder(@PathVariable String orderId) {
// Only matches order IDs like AB123456
return "orderDetails";
}

Request Mapping Fallbacks

You can create fallback mappings using lower specificity:

java
@GetMapping("/**")
public String handleFallback() {
// This will catch any GET requests not handled by other methods
return "notFound";
}

Summary

@RequestMapping is a versatile annotation that forms the backbone of Spring MVC request handling. With it, you can:

  • Map web requests to controllers and methods
  • Restrict mappings to specific HTTP methods
  • Extract data from URLs with path variables
  • Process request parameters
  • Control content negotiation with media types
  • Create RESTful APIs with clear, consistent patterns

Understanding @RequestMapping and its specialized variants like @GetMapping and @PostMapping is essential for building effective Spring MVC applications. These annotations allow you to create clean, organized, and maintainable web endpoints.

Additional Resources

Exercises

  1. Create a basic Spring MVC controller that handles CRUD operations for a Product entity with appropriate request mappings.
  2. Implement a REST API that accepts and returns both JSON and XML formats for a Customer resource.
  3. Build a file management system that allows uploading, listing, and downloading files with proper request mappings.
  4. Create a controller with versioned API endpoints using different versioning strategies (URL path, request parameters, headers).
  5. Implement a form wizard that spans multiple pages, using request mappings to handle the different steps of the process.


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