Introduction
If you've read anything about Spring, developed a project, or was even remotely interested in how it works, you've been introduced to the @RequestMapping
annotation.
It's one of the basic annotations in Spring which maps HTTP requests (URLs) with methods:
@RequestMapping("/")
public void helloWorld() {
return "Hello World!";
}
It allows us to run methods and pieces of code each time an endpoint is hit by the end-user with an HTTP request. In this case, it's a simple root mapping that returns the String "Hello World!" when the root endpoint is hit.
The @RequestMapping Annotation
The @RequestMapping
annotation itself offers more than shown in the previous example. Let's briefly cover some of the basic use cases of the annotation.
@RequestMapping Scopes
The mapping can be assigned at a class level or a method level:
@RequestMapping("/admin")
public class AdminController() {
@RequestMapping("/adminPanel")
public String adminPanel() {
return "adminPanel.html";
}
@RequestMapping("/statistics")
public String statistics() {
return "statistics.html";
}
}
Here, the /admin
mapping is assigned on the class level, whereas the /adminPanel
and /statistics
mappings are assigned to methods. The class-level mapping will be used as a prefix for all of these method-level mappings: /admin/adminPanel
and /admin/statistics
.
This way, we can segregate our application into logical parts. For an example, all administrator related logic will be behind the /admin
wall, which only administrator users can enter. All non-administrator users will be redirected to the client side of the application.
Multiple Mappings per Method
If need be, we can assign multiple mappings to a single method, such as:
@RequestMapping({"/", "/index", "/home"})
public void helloWorld() {
return "Hello World!";
}
Hitting any of these endpoints will result in the helloWorld()
method handling our request.
Request Methods
The most common attribute used is the method
attribute which allows us to specify and narrow down the request method that our method handles (GET, POST, DELETE, etc.) :
@RequestMapping(value = "/addProduct", method = RequestMethod.POST)
public String addProductPost(@ModelAttribute("product") Product product) {
// some code
}
If we don't specify a request method, the default request method is GET
.
Request Headers
We can further specify the mapping by defining one or more headers. In this case, the helloWorld()
method will handle the request if its content-type
is text/plain
or text/html
:
@RequestMapping(value = "/header", headers = {"content-type=text/plain", "content-type=text/html"})
String helloWorld() {
return "Hello World!";
}
Path Variables
A lot of websites rely on path variables to showcase a specific product, page, profile, etc. to the end-user, such as example.com/viewProduct/324
, where 324
is the product ID:
@RequestMapping("/viewProduct/{productId}")
public String viewProduct(@PathVariable int productId, Model model) {
Product product = productService.getProductById(productId);
model.addAttribute("product", product);
return "viewProduct";
}
Here, after the mapping, we encase the path variable with curly brackets ({}
) and assign that value to our productId
. The @PathVariable
annotation takes care of this for us. Then, we can find the product via our ProductService
and display it to the end-user.
Of course, the same approach can be used for other variables such as:
@RequestMapping("/users/{username}")
public String viewUser(@PathVariable("username") String username) {
Profile profile = profileService.getProfileByUsername(username);
// rest of the code
}
Request Parameters
Very similar to path variables, many applications rely on request parameters to alter the state of the page. We can use the @RequestParam
annotation to bind a request parameter to a variable:
@RequestMapping("/login")
public String login(@RequestParam(value = "error", required = false) String error, @RequestParam(value = "logout", required = false) String logout, Model model) {
if (error != null) {
model.addAttribute("error", "Wrong username or password!");
}
if (logout != null) {
model.addAttribute("msg", "You have successfully logged out!");
}
return "login";
}
Producible and Consumable
Using the @RequestMapping
annotation, we can also produce or consume media types. To produce a media type such as "JSON", you can use the produces
attribute alongside the @ResponseBody
annotation, whereas to consume a media type you can use the consumes
attribute:
@RequestMapping("/annotations")
public class SomeController {
@RequestMapping(value = "/produce", produces = {"application/json"})
@ResponseBody
String produces() {
return "Here's how you use the 'produces' attribute";
}
@RequestMapping(value = "/consume", consumes = {"application/JSON", "application/XML"})
String consume() {
return "Here's how you use a 'consume' attribute";
}
}
Composed @RequestMapping Variants
A single mapping can correspond to different methods if we ensure there's no ambiguity as to which method should handle which version of the mapping.
We can have multiple methods with the same mapping if their request method is different:
@RequestMapping("/hello")
public String hello() {
return "hello";
}
@RequestMapping(value = "/hello", method = RequestMethod.POST)
public String helloPost() {
return "hello post";
}
@RequestMapping(value = "/hello", method = RequestMethod.PUT)
public String helloGet() {
return "hello put";
}
@RequestMapping(value = "/hello", method = RequestMethod.DELETE)
public String helloDelete() {
return "hello delete";
}
This same method has 4 different variations:
- Default (GET)
- POST
- PUT
- DELETE
With more variations, the readability of code gets reduced, and this quite honestly is already verbose for a simple mapping. Request mappings in bigger applications can get very complex and convoluted, and we'd want to keep it simpler and more readable.
For this purpose, we're introduced to several new annotations, the @RequestMapping
variants:
@GetMapping
@PostMapping
@DeleteMapping
@PutMapping
@PatchMapping
There are quite literally just shortcut versions for the mappings above where @PostMapping("/hello")
is equal to @RequestMapping(value="/hello", RequestMethod.POST)
.
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
Taking a closer look at how these annotations are defined, we can get a clear idea of what they do:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {
@AliasFor(annotation = RequestMapping.class)
String name() default "";
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
@AliasFor(annotation = RequestMapping.class)
String[] path() default {};
@AliasFor(annotation = RequestMapping.class)
String[] params() default {};
@AliasFor(annotation = RequestMapping.class)
String[] headers() default {};
@AliasFor(annotation = RequestMapping.class)
String[] produces() default {};
}
That being said, we can rewrite the examples above:
@GetMapping
public String hello() {
return "hello";
}
@PostMapping
public String helloPost() {
return "hello post";
}
@PutMapping
public String helloGet() {
return "hello put";
}
@DeleteMapping
public String helloDelete() {
return "hello delete";
}
As this is both a simpler, more readable and newer way of writing annotations, it's generally preferred to write them like this. If need be, you can still define any other attributes such as the value
, params
, path
, etc.
Conclusion
@RequestMapping
is a quintessential annotation in the Spring framework which allows us to map HTTP requests with methods we'd wish to run.
As of Spring 4.3, it's preferred and advised to use the shortcuts made for this annotation for a more readable and less verbose codebase.