The annotation @RequestMapping
@RequestMapping is used to map request URLs to specific controllers.
Definition of RequestMappingVersion: 7.0.5 package org.springframework.web.bind.annotation;
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
@Reflective(ControllerMappingReflectiveProcessor.class)
public @interface RequestMapping {
String name() default ""; 1
@AliasFor("path")
String[] value() default {}; 2
@AliasFor("value")
String[] path() default {}; 3
RequestMethod[] method() default {}; 4
String[] params() default {}; 5
String[] headers() default {}; 6
String[] consumes() default {}; 7
String[] produces() default {}; 8
String version() default ""; 9
}
All elements of @RequestMapping annotation are optional.
It's annotated with @Target(value={METHOD,TYPE}), so it can be used on class level or method level.
If @RequestMapping specifies a path on class level then all paths in the methods are relative.
@RequestMapping on the class level is not required. Without it, all paths are simply absolute, and not relative
The handler methods without @RequestMapping won't be mapped, even their enclosing class has @Controller and a valid @RequestMapping annotations.
Elements of @RequestMapping
String[] value
It's the URL mapping expression:
@RequestMapping("/users")
@Controller
public class UserController{
...
}
Multiple URLs can be specified:
@RequestMapping({"/users", "/clients"})
@Controller
public class UserController{
...
}
It may contain URI templates:
@RequestMapping("/users")
@Controller
public class UserController{
@RequestMapping("/{userId}")
public String handle(....){
....
}
}
The method handle() will map to /users/{userId}
URI Template Patterns may contain regex:
@RequestMapping("/{userId:[0-9]+}")
URI templates can be captured by handler's method parameter annotated with @PathVariable:
@RequestMapping("/{userId}")
public void handle(@PathVariable("userId") String userId) {
// ...
}
A method without @RequestMapping won't be mapped, even though enclosing class annotations are valid. For example the following code will not map the method handleAllUsersRequest() and will return 404 error for the request /users:
@Controller
@RequestMapping("/users")
public class UserController {
public String handleAllUsersRequest(){
.....
}
}
To fix above mapping (for the request /users):
@Controller
@RequestMapping("/users")
public class UserController {
@RequestMapping
public String handleAllUsersRequest(){
.....
}
}
On class level @RequestMapping("") or just @RequestMapping will map to root url "/" (example here).
If on class level no @RequestMapping annotation is used then on method level @RequestMapping("") will map to the root url ("/"), whereas, empty @RequestMapping (without path) will act as a fall back and will map to all URLs for which specific matches are not available (example here).
RequestMethod[] method
The HTTP request methods, this handler can support:
@Controller
@RequestMapping("/users")
public class UserController {
@RequestMapping(value= "{id}", method = {RequestMethod.GET, RequestMethod.DELETE})
public String handle(......){
//..
}
}
Per good design principles, it's better to define separate handler methods based on HTTP methods.
Different handler methods can uniquely be defined based on different HTTP methods (no ambiguity), even if they have same request URL path:
@Controller
@RequestMapping("/users")
public class UserController {
@RequestMapping(value= "{id}", method = {RequestMethod.GET})
public String handleGet(.....){
//..
}
@RequestMapping(value= "{id}", method = {RequestMethod.DELETE})
public String handleDelete(.....){
//..
}
}
String[] params
The query string parameters. The annotated method will only be mapped if the query string matches.
This is another level for defining handler methods uniquely (no ambiguity) or in other words to narrow down the primary mapping. In the following example the request /users?id=4 will be mapped to method handleUserId4 and the request /user?id=10 will be mapped to method handleUserId10:
@Controller
@RequestMapping("/users")
public class UserControllerParams {
@RequestMapping(params = "id=4")
public String handleUserId4(.....) {
System.out.println("got param id = 4");
return "view-name";
}
@RequestMapping(params = "id=10")
public String handleUserId10(....) {
System.out.println("got param id = 10");
return "view-name";
}
}
We don't have to necessarily capture the query param using @RequestParam as each method will only be called when params matches, that means we can safely use hardcoded param values inside the handler method.
Defining multiple query params:
@Controller
@RequestMapping("/users")
public class UserControllerParams {
@RequestMapping(params = {"state=TX", "dept=IT"})
public String handleRequest(.....) {
....
return "view-name";
}
}
We can skip the query param value part. In that case all request having the specified param name will be mapped regardless of their values:
@Controller
@RequestMapping("/users")
public class UserControllerParams {
@RequestMapping(params = "dept")
public String handleRequest(.....) {
....
return "view-name";
}
}
It's just like params element but instead of query params, it is used to specify HTTP headers key-value pair. That uniquely defines handlers (narrowing the primary mapping):
@Controller
@RequestMapping("/users")
public class UserControllerHeader {
@RequestMapping(headers = "id=4")
public String handleAllUsersRequest() {
System.out.println("got header id = 4");
return "view-name";
}
@RequestMapping(headers = "id=10")
public String handleAllUsersRequest2() {
System.out.println("got header id = 10");
return "view-name";
}
}
String[] consumes
It defines the consumable media types of the mapped request, narrowing the primary mapping.
It's based on Content Negotiation specifications.
@Controller
@RequestMapping("/users")
public class UserControllerConsume {
@RequestMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public String handleJson(@RequestBody String s) {
System.out.println("json body : " + s);
return "view-name";
}
@RequestMapping(consumes = MediaType.APPLICATION_XML_VALUE)
public String handleXML(@RequestBody String s) {
System.out.println("xml body " + s);
return "view-name";
}
}
Expressions can be negated by using the "!" operator, as in "!text/plain", which matches all requests with a Content-Type other than "text/plain".
Mapping Requests with @RequestMapping
What is @RequestBody?
Annotation indicating a method parameter should be bound to the body of the web request. The body of the request is passed through an HttpMessageConverter to resolve the method argument depending on the content type of the request.
String[] produces
The producible media types of the mapped request, narrowing the primary mapping.
@Controller
@RequestMapping("/users")
public class UserControllerProduce {
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public @ResponseBody String handleJson() {
System.out.println("Got json request");
return "{ \"userName\": \"Joe\"}";
}
@RequestMapping(produces = MediaType.APPLICATION_XML_VALUE)
public @ResponseBody String handleXML() {
System.out.println("Got xml request");
return "<userName>Joe</userName>";
}
}
Expressions can be negated by using the "!" operator, as in "!text/plain", which matches all requests with a Accept other than "text/plain".
String[] path
Alias for 'value'
Ant-style path patterns are also supported (e.g. /**/users).
This example will match any URL ending with users e.g. /dept1/dept2/dept3/dept4/dept5/users
@Controller
@RequestMapping("/**/users")
public class UserControllerPath {
@RequestMapping
public void handleAllUsersRequest(HttpServletRequest request){
System.out.println(request.getRequestURL());
}
}
Also check out this to understand URI pattern matching in details.
String name
Assign a name to this mapping.
Placeholders in path pattern
@RequestMapping's value element can also have a placeholder ${...} pattern against a property source. Check out an example here.
String version (Spring 7+ new @RequestMapping element)
Spring Framework 7 introduces native, declarative API versioning support.
Versioning is implemented via a version attribute in @RequestMapping and related annotations (@GetMapping, @PostMapping, etc.).
Spring 7 introduces a declarative API versioning model that replaces manual path or header handling. By using the version attribute within @RequestMapping, developers can natively manage multiple API iterations.
- Flexible Resolution: Versions can be automatically extracted from
URL paths, Request Headers, query params or Media Types.
- Global Configuration: The
ApiVersionConfigurer allows for setting default versions and handling Deprecation or Sunset headers. query param.
- Tooling Integration: Improved support for
Spring Doc and OpenAPI to automatically generate version-specific documentation.
@EnableWebMvc
@Configuration
public class WebConfigApiVersioning implements WebMvcConfigurer {
.....
@Override
public void configureApiVersioning(ApiVersionConfigurer configurer) {
configurer.useQueryParam("v")
.addSupportedVersions("2.0", "3.0", "3.1");
}
}
@RequestMapping(version = "2")
public String handleAllUsersRequest(){
.....
}
@RequestMapping(version = "3+")
public String handleAllUsersRequest3(){
..
}
Example ProjectDependencies and Technologies Used: - spring-webmvc 7.0.5 (Spring Web MVC)
Version Compatibility: 3.2.9.RELEASE - 7.0.5 Version compatibilities of spring-webmvc with this example: Versions in green have been tested.
- spring-test 7.0.5 (Spring TestContext Framework)
- json-path 2.10.0 (A library to query and verify JSON)
- jakarta.servlet-api 6.1.0 (Jakarta Servlet API documentation)
- junit-jupiter-engine 6.0.2 (Module "junit-jupiter-engine" of JUnit)
- hamcrest 3.0 (Core API and libraries of hamcrest matcher framework)
- JDK 25
- Maven 3.9.11
|