Using the Spring @RequestMapping Annotation
As often as Springs @RequestMapping annotation is, few recognize its versatility. Here, we see that on display when used to map Spring MVC controller methods.
Join the DZone community and get the full member experience.
Join For Free@RequestMapping is one of the most common annotation used in Spring Web applications. This annotation maps HTTP requests to handler methods of MVC and REST controllers.
In this post, you’ll see how versatile the @RequestMapping annotation is when used to map Spring MVC controller methods.
Request Mapping Basics
In Spring MVC applications, the RequestDispatcher (Front Controller Below) servlet is responsible for routing incoming HTTP requests to handler methods of controllers.
When configuring Spring MVC, you need to specify the mappings between the requests and handler methods.
To configure the mapping of web requests, you use the @RequestMapping annotation.
The @RequestMapping annotation can be applied to class-level and/or method-level in a controller.
The class-level annotation maps a specific request path or pattern onto a controller. You can then apply additional method-level annotations to make mappings more specific to handler methods.
Here is an example of the @RequestMapping annotation applied to both class and methods.
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping("/")
String get() {
//mapped to hostname:port/home/
return "Hello from get";
}
@RequestMapping("/index")
String index() {
//mapped to hostname:port/home/index/
return "Hello from index";
}
}
With the preceding code, requests to /home will be handled by get() while request to /home/index will be handled by index().
@RequestMapping With Multiple URIs
You can have multiple request mappings for a method. For that add one @RequestMapping annotation with a list of values.
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = {
"",
"/page",
"page*",
"view/*,**/msg"
})
String indexMultipleMapping() {
return "Hello from index multiple mapping.";
}
}
As you can see in this code, @RequestMapping supports wildcards and ant-style paths. For the preceding code, all these URLs will be handled by indexMultipleMapping().
- localhost:8080/home
- localhost:8080/home/
- localhost:8080/home/page
- localhost:8080/home/pageabc
- localhost:8080/home/view/
- localhost:8080/home/view/view
@RequestMapping With @RequestParam
The @RequestParam annotation is used with @RequestMapping to bind a web request parameter to the parameter of the handler method.
The @RequestParam annotation can be used with or without a value. The value specifies the request param that needs to be mapped to the handler method parameter, as shown in this code snippet.
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/id")
String getIdByValue(@RequestParam("id") String personId) {
System.out.println("ID is " + personId);
return "Get ID from query string of URL with value element";
}
@RequestMapping(value = "/personId")
String getId(@RequestParam String personId) {
System.out.println("ID is " + personId);
return "Get ID from query string of URL without value element";
}
}
In Line 6 of this code, the request param id will be mapped to the personId parameter personId of thegetIdByValue() handler method.
The value element of @RequestParam can be omitted if the request param and handler method parameter names are same, as shown in Line 11.
The required element of @RequestParam defines whether the parameter value is required or not.
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/name")
String getName(@RequestParam(value = "person", required = false) String personName) {
return "Required element of request param";
}
}
In this code snippet, as the required element is specified as false, the getName() handler method will be called for both of these URLs:
- /home/name?person=xyz
- /home/name
The default value of the @RequestParam is used to provide a default value when the request param is not provided or is empty.
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/name")
String getName(@RequestParam(value = "person", defaultValue = "John") String personName) {
return "Required element of request param";
}
}
In this code, if the person request param is empty in a request, the getName() handler method will receive the default value John as its parameter.
Using @RequestMapping With HTTP Methods
The Spring MVC @RequestMapping annotation is capable of handling HTTP request methods, such as GET, PUT, POST, DELETE, and PATCH.
By default, all requests are assumed to be of HTTP GET type.
In order to define a request mapping with a specific HTTP method, you need to declare the HTTP method in@RequestMapping using the method element as follows.
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(method = RequestMethod.GET)
String get() {
return "Hello from get";
}
@RequestMapping(method = RequestMethod.DELETE)
String delete() {
return "Hello from delete";
}
@RequestMapping(method = RequestMethod.POST)
String post() {
return "Hello from post";
}
@RequestMapping(method = RequestMethod.PUT)
String put() {
return "Hello from put";
}
@RequestMapping(method = RequestMethod.PATCH)
String patch() {
return "Hello from patch";
}
}
In the code snippet above, the method element of the @RequestMapping annotations indicates the HTTP method type of the HTTP request.
All the handler methods will handle requests coming to the same URL ( /home), but will depend on the HTTP method being used.
For example, a POST request to /home will be handled by the post() method. While a DELETE request to/home will be handled by the delete() method.
You can see how Spring MVC will map the other methods using this same logic.
Using @RequestMapping With Producible and Consumable
The request mapping types can be narrowed down using the produces and consumes elements of the@RequestMapping annotation.
In order to produce the object in the requested media type, you use the produces element of @RequestMapping in combination with the @ResponseBody annotation.
You can also consume the object with the requested media type using the consumes element of@RequestMapping in combination with the @RequestBody annotation.
The code to use producible and consumable with @RequestMapping is this.
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/prod", produces = {
"application/JSON"
})
@ResponseBody
String getProduces() {
return "Produces attribute";
}
@RequestMapping(value = "/cons", consumes = {
"application/JSON",
"application/XML"
})
String getConsumes() {
return "Consumes attribute";
}
}
In this code, the getProduces() handler method produces a JSON response. The getConsumes() handler method consumes JSON as well as XML present in requests.
@RequestMapping With Headers
The @RequestMapping annotation provides a header element to narrow down the request mapping based on headers present in the request.
You can specify the header element as myHeader = myValue.
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/head", headers = {
"content-type=text/plain"
})
String post() {
return "Mapping applied along with headers";
}
}
In the above code snippet, the headers attribute of the @RequestMapping annotation narrows down the mapping to the post() method. With this, the post() method will handle requests to /home/head whose content-typeheader specifies plain text as the value.
You can also indicate multiple header values like this:
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/head", headers = {
"content-type=text/plain",
"content-type=text/html"
}) String post() {
return "Mapping applied along with headers";
}
}
Here it implies that both text/plain as well as text/html are accepted by the post() handler method.
@RequestMapping With Request Parameters
The params element of the @RequestMapping annotation further helps to narrow down request mapping. Using the params element, you can have multiple handler methods handling requests to the same URL, but with different parameters.
You can define params as myParams = myValue. You can also use the negation operator to specify that a particular parameter value is not supported in the request.
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/fetch", params = {
"personId=10"
})
String getParams(@RequestParam("personId") String id) {
return "Fetched parameter using params attribute = " + id;
}
@RequestMapping(value = "/fetch", params = {
"personId=20"
})
String getParamsDifferent(@RequestParam("personId") String id) {
return "Fetched parameter using params attribute = " + id;
}
}
In this code snippet, both the getParams() and getParamsDifferent() methods will handle requests coming to the same URL (/home/fetch) but will execute depending on the params element.
For example, when the URL is /home/fetch?id=10, the getParams() handler method will be executed with the id value 10.. For the URL, localhost:8080/home/fetch?personId=20, the getParamsDifferent() handler method gets executed with the id value 20.
Using @RequestMapping With Dynamic URIs
The @RequestMapping annotation is used in combination with the @PathVaraible annotation to handle dynamic URIs. In this use case, the URI values can act as the parameter of the handler methods in the controller. You can also use regular expressions to only accept the dynamic URI values that match the regular expression.
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping(value = "/fetch/{id}", method = RequestMethod.GET)
String getDynamicUriValue(@PathVariable String id) {
System.out.println("ID is " + id);
return "Dynamic URI parameter fetched";
}
@RequestMapping(value = "/fetch/{id:[a-z]+}/{name}", method = RequestMethod.GET)
String getDynamicUriValueRegex(@PathVariable("name") String name) {
System.out.println("Name is " + name);
return "Dynamic URI parameter fetched using regex";
}
}
In this code, the method getDynamicUriValue() will execute for a request to localhost:8080/home/fetch/10. Also, the id parameter of the getDynamicUriValue() handler method will be populated with the value 10 dynamically.
The method getDynamicUriValueRegex() will execute for a request to localhost:8080/home/fetch/category/shirt. However, an exception will be thrown for a request to /home/fetch/10/shirt as it does not match the regular expression.
@PathVariable works differently from @RequestParam. You use @PathVariable to obtain the values of the query parameters from the URI. On the other hand, you use @RequestParam to obtain the parameter values from the URI template.
The @RequestMapping Default Handler Method
In the controller class, you can have default handler method that gets executed when there is a request for a default URI.
Here is an example of a default handler method.
@RestController
@RequestMapping("/home")
public class IndexController {
@RequestMapping()
String
default () {
return "This is a default method for the class";
}
}
In this code, a request to /home will be handled by the default() method as the annotation does not specify any value.
@RequestMapping Shortcuts
Spring 4.3 introduced method-level variants, also known as composed annotations of @RequestMapping. The composed annotations better express the semantics of the annotated methods. They act as wrapper to@RequestMapping and have become the standard ways of defining the endpoints.
For example, @GetMapping is a composed annotation that acts as a shortcut for @RequestMapping(method =RequestMethod.GET).
The method level variants are:
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
The following code shows using the composed annotations.
@RestController
@RequestMapping("/home")
public class IndexController {
@GetMapping("/person")
public @ResponseBody ResponseEntity < String > getPerson() {
return new ResponseEntity < String > ("Response from GET", HttpStatus.OK);
}
@GetMapping("/person/{id}")
public @ResponseBody ResponseEntity < String > getPersonById(@PathVariable String id) {
return new ResponseEntity < String > ("Response from GET with id " + id, HttpStatus.OK);
}
@PostMapping("/person")
public @ResponseBody ResponseEntity < String > postPerson() {
return new ResponseEntity < String > ("Response from POST method", HttpStatus.OK);
}
@PutMapping("/person")
public @ResponseBody ResponseEntity < String > putPerson() {
return new ResponseEntity < String > ("Response from PUT method", HttpStatus.OK);
}
@DeleteMapping("/person")
public @ResponseBody ResponseEntity < String > deletePerson() {
return new ResponseEntity < String > ("Response from DELETE method", HttpStatus.OK);
}
@PatchMapping("/person")
public @ResponseBody ResponseEntity < String > patchPerson() {
return new ResponseEntity < String > ("Response from PATCH method", HttpStatus.OK);
}
}
In this code, each of the handler methods are annotated with the composed variants of @RequestMapping. Although each variant can be interchangeably used with @RequestMapping with the method attribute, it’s considered a best practice to use the composed variant —primarily because the composed annotations reduce the configuration metadata on the application side and the code is more readable.
@RequestMapping Conclusion
As you can see in this post, the @RequestMapping annotation is very versatile. You can use this annotation to configure Spring MVC to handle a variety of use cases. It can be used to configure traditional web page requests as well as RESTFul web services in Spring MVC.
Published at DZone with permission of John Thompson, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments