You can use the @RequestMapping annotation to map requests to controller methods. It has various attributes to match by URL, HTTP method, request parameters, headers, headers, and data types. You can use it at the class level to express general mappings, or at the method level to narrow it down to a specific endpoint mapping.

There are also HTTP method-specific options for shortening the @RequestMapping annotation:

  • @GetMapping

  • @PostMapping

  • @PutMapping

  • @DeleteMapping

  • @PatchMapping

Abbreviations are custom annotations that are specified because, perhaps, most controller methods need to be mapped to a specific HTTP -method rather than using the @RequestMapping annotation, which maps to all HTTP methods by default. Expressing common class-level mappings still requires @RequestMapping.

The following example has type- and method-level mappings:

Java

@RestController
@RequestMapping("/persons")
class PersonController {
    @GetMapping("/{id}")
    public Person getPerson(@PathVariable Long id) {
        // ...
    }
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void add(@RequestBody Person person) {
        // ...
    }
}
Kotlin
 
@RestController
@RequestMapping("/persons")
class PersonController {
    @GetMapping("/{id}")
    fun getPerson(@PathVariable id: Long): Person {
        // ...
    }
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    fun add(@RequestBody person: Person) {
        // ...
    }
}

URI Patterns

Methods annotated with @RequestMapping can be mapped using URL patterns. There are two alternatives:

  • PathPattern - a pre-parsed pattern mapped to a URL path, also pre-parsed as a PathContainer. Designed for use in the Internet environment, this solution works efficiently with encoding and path parameters and provides efficient matching.

  • AntPathMatcher - string matching templates with string path. This original solution is also used in Spring configuration to select resources in the classpath, in the file system and other places. It is less efficient, and entering a string path makes it difficult to effectively deal with encoding and other URL issues.

PathPattern is the recommended solution for the web -applications, and this is the only option when working in Spring WebFlux. Prior to version 5.3, AntPathMatcher was the only option when working in Spring MVC and remains the default. However, PathPattern can be activated in an MVC configuration.

PathPattern supports the same pattern syntax as AntPathMatcher. In addition, it also supports a capture pattern like {*spring} to match 0 or more path segments at the end of a path. PathPattern also restricts the use of the ** character to match multiple path segments so that it is only allowed at the end of the pattern. This avoids ambiguity in choosing the best matching pattern for a given query. Full pattern syntax is provided in the sections on PathPattern and AntPathMatcher.

Some examples of templates:

  • "/resources/ima?e.png" - matches one character in the path segment

  • "/resources/*.png" - matches zero or more characters in path segment

  • "/resources/**" – matches multiple path segments

  • "/projects/{project}/versions" – matches the path segment and writes it as a variable

  • "/projects/{ project:[a-z]+}/versions" – matching and capturing a variable using a regular expression

Captured URI variables can be accessed using an annotation @PathVariable. For example:

Java

@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
    // ...
}
Kotlin

@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
    // ...
}

You can declare URI variables at the class and method level, as shown in the following example:

Java

@Controller
@RequestMapping("/owners/{ownerId}")
public class OwnerController {
    @GetMapping("/pets/{petId}")
    public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
        // ...
    }
}
Kotlin

@Controller
@RequestMapping("/owners/{ownerId}")
class OwnerController {
    @GetMapping("/pets/{petId}")
    fun findPet(@PathVariable ownerId: Long, @PathVariable petId: Long): Pet {
        // ...
    }
}

URI variables are automatically converted to the appropriate type, or a TypeMismatchException is thrown. Simple types (int, long, Date, and so on) are supported by default, but support for any other data type can be registered. See Type Conversion and DataBinder.

You can explicitly name URI variables (for example, @PathVariable("customId")), but you can also omit this information if the names are the same and your code is compiled using debugging information or with the -parameters compiler flag in Java 8.

Syntax {varName:regex} declares a URI variable using a regular expression, which has the syntax {varName:regex}. For example, if given the URL "/spring-web-3.0.5.jar", then the following method retrieves the file name, version, and extension:

Java

@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
public void handle(@PathVariable String name, @PathVariable String version, @PathVariable String ext) {
    // ...
}
    
Kotlin

@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
fun handle(@PathVariable name: String, @PathVariable version: String, @PathVariable ext: String) {
    // ...
}

URI path templates can also contain embedded placeholders ${...}, which are recognized when run using PropertySourcesPlaceholderConfigurer in local, system property sources, property sources environment, etc. You can use this, for example, to parameterize a base URL based on some external configuration.

Template comparison

If several patterns match a URL, you need to select the most suitable one . This is done in one of the following ways, depending on whether the syntactically parsed PathPattern is allowed to be used or not:

Both help sort the templates, with the more specific ones at the top. A pattern is less specific if it has fewer variable URIs (counts as 1), single wildcards (counts as 1), and double wildcards (counts as 2). If the indicators are equal, the longer template is selected. Given the same metrics and length, the pattern that has more variable URIs than wildcards is selected.

The default display pattern (/**) is excluded from the count and is always sorted last . Additionally, patterns with prefixes (such as /public/**) are considered less specific than other patterns that do not contain double wildcards.

For complete information, follow these See the links above for descriptions of pattern comparators.

Suffix matching

As of version 5.3, Spring MVC no longer defaults to pattern matching with .* suffixes if a controller mapped to /person is also implicitly mapped to /person.*. As a consequence, path extensions are no longer used to interpret the requested content type for the response - for example, /person.pdf, /person.xml, and so on.

Using file extensions in this way was necessary when browsers sent Accept headers that were difficult to interpret consistently. This is no longer necessary today, and using the Accept header should be the preferred choice.

Over time, using file name extensions has proven problematic for a variety of reasons. This may cause ambiguity when overlaying using variable URIs, path parameters, and URI encoding. Making the case for URL-based authorization and security (see next section for more details) also becomes more complex.

To completely disable the use of path extensions in versions prior to 5.3, set the following:

  • useSuffixPatternMatching(false)

  • favorPathExtension(false)

Having a way to request content types other than the "Accept" header can be useful, for example, when entering a URL in a browser. A safe alternative to path expansion is to use the query parameters strategy. If you must use file extensions, consider restricting them to a list of explicitly registered extensions via the mediaTypes property of the ContentNegotiationConfigurer.

Suffix Match and RFD

Vector Reflected File Download (RFD) attacks are similar to XSS in that they rely on the request input (such as a request parameter and a URI variable) being reflected in the response. However, instead of inserting JavaScript into the HTML, the RFD attack vector is based on switching the browser to perform the download and treating the response as an executable script when you double-click it afterwards.

In Spring MVC, methods marked with the @ResponseBody annotation and ResponseEntity methods are at risk because they can render different types of content that clients can request via URL path extensions. Disabling suffix pattern matching and using path extensions to match content reduces the risk, but is not sufficient to prevent RFD attacks.

To prevent RFD attacks, Spring MVC adds a Content-Disposition:inline;filename=f.txtheader before rendering the response body to offer a fixed and secure download file. This is only done if the URL path contains a file extension that is not resolved as safe and has not been explicitly registered for content negotiation. However, this can have side effects if the URLs are entered directly into the browser.

Many common path extensions are allowed as safe by default. Applications with custom HttpMessageConverter implementations can explicitly register file extensions for content negotiation to avoid adding a Content-Disposition header for these extensions.

Additional considerations related with RFD, see "CVE-2015-5211".

Consumed Data Types

You can narrow the query display based on the Content-Type query, as shown in the following example:

Java

@PostMapping(path = "/pets", consumes = "application/json") 
public void addPet( @RequestBody Pet pet) {
    // ...
}
  1. Using the consumes attribute to narrow the display range by content type.
Kotlin

@PostMapping("/pets", consumes = ["application/json" ]) 
fun addPet(@RequestBody pet: Pet) {
    // ...
}
  1. Use the consumes attribute to narrow the display range by content type.

Attribute consumes also supports negation expressions - for example, !text/plain means any content type other than text/plain.

You can declare the general consumes attribute at the class level. However, unlike most other request mapping attributes, when used at the class level, the consumes attribute at the method level overrides rather than extends the class-level declaration.

MediaType provides constants for commonly used media types, such as APPLICATION_JSON_VALUE and APPLICATION_XML_VALUE.

Produced Data Types

You can narrow the range of requests that are displayed based on the Accept request header and the list of content types that the controller method produces, as shown in the following example:

Java

@GetMapping(path = "/pets/{petId}", produces = "application/json") 
@ResponseBody
public Pet getPet(@PathVariable String petId) {
    // ...
}
  1. Use the produces attribute to narrow the display range by content type.
Kotlin

@GetMapping("/pets/{petId}", produces = ["application/json"]) 
@ResponseBody
fun getPet( @PathVariable petId: String): Pet {
// ...
}
  1. Using the produces attribute to narrow the display range by content type.

The media type can determine the character set. Negable expressions are supported - for example, !text/plain means any content type other than "text/plain".

You can declare a generic produces attribute on class level. However, unlike most other request mapping attributes, when used at the class level, the produces attribute at the method level overrides rather than extends the class-level declaration.

MediaType provides constants for commonly used media types, such as APPLICATION_JSON_VALUE and APPLICATION_XML_VALUE.

Parameters, headers

You can narrow the display range of queries based on the conditions of the query parameters. You can check for the presence of a query parameter(myParam), its absence (!myParam) or a specific value(myParam=myValue). The following example shows how to check for the presence of a specific value:

Java

@GetMapping(path = "/pets/{petId}", params = "myParam=myValue ") 
public void findPet(@PathVariable String petId) {
    // ...
}
  1. Checks whether myParam is equal to myValue.
Kotlin

@GetMapping("/pets/{petId}", params = ["myParam=myValue"]) 
fun findPet(@PathVariable petId: String) {
// ...
}
  1. Checks whether myParam is equal to the value of myValue.

The same can be used with request header conditions , as shown in the following example:

Java

@GetMapping(path = "/pets", headers = "myHeader=myValue") 
public void findPet(@PathVariable String petId) {
// ...
}
  1. Checks whether myHeader is equal to myValue.
Kotlin

@GetMapping("/pets", headers = ["myHeader=myValue"]) 
fun findPet( @PathVariable petId: String) {
    // ...
}
You can match Content-Type and Accept with headers condition, but it is better to use consumes and produces instead.

HTTP methods HEAD, OPTIONS

@GetMapping (and @RequestMapping(method=HttpMethod.GET)) transparently support the HTTP HEAD method for mapping requests. The controller methods do not need to be changed. The response wrapper function used in javax.servlet.http.HttpServlet ensures that the Content-Length header is set to the number of bytes written (without actually writing to the response).

@GetMapping (and @RequestMapping(method=HttpMethod.GET)) implicitly map to and support the HEAD HTTP method. A request using the HTTP HEAD method is processed in the same way as an HTTP GET method, except that instead of writing a body, the number of bytes is counted and the header Content-Length is set.

By default, the HTTP OPTIONS method is handled by setting the Allow response header to the list of HTTP methods listed in all methods marked with the @RequestMapping annotation that have matching URL patterns.

In the case of the @RequestMapping annotation without declaring HTTP methods, the Allow header is set to GET, HEAD, POST, PUT, PATCH, DELETE , OPTIONS. Controller methods should always declare supported HTTP methods (for example, using HTTP method-specific variants: @GetMapping, @PostMapping, and others).

You can explicitly map a method annotated with @RequestMapping to the HEAD HTTP method and the OPTIONS HTTP method, but in ordinary cases this is not necessary.

Custom annotations

Spring MVC supports the use of compound annotations to display queries. These are annotations that are themselves @RequestMapping meta-annotations and are composed to re-declar a subset (or all) of the attributes marked with the @RequestMapping annotation for a narrower, more specific purpose.

@GetMapping, @PostMapping, @PutMapping, @DeleteMapping and @PatchMapping are examples of compound annotations. These are provided because it is likely that most controller methods will need to be mapped to a specific HTTP method instead of using the @RequestMapping annotation, which maps to all HTTP methods by default. If you need an example of compound annotations, take a look at how they are declared.

Spring MVC also supports custom query mapping attributes with custom query mapping logic. This is a more advanced option that requires subclassing RequestMappingHandlerMapping and overriding the getCustomMethodCondition method, where you can check the custom attribute and return your own RequestCondition.

Explicit registration

You can programmatically register handler methods, which can be used for dynamic registration or in more complex cases, such as if there are different instances of the same handler under different URLs. The following example registers a handler method:

Java

@Configuration
public class MyConfig {
    @Autowired
    public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) 
            throws NoSuchMethodException {
        RequestMappingInfo info = RequestMappingInfo
                .paths("/user/{id}").methods(RequestMethod.GET).build(); 
        Method method = UserHandler.class.getMethod("getUser", Long.class); 
        mapping.registerMapping(info, handler, method); 
    }
}
  1. Inject the target handler and handler mapping for controllers.
  2. Prepare the request mapping metadata.
  3. Get the handler method.
  4. Add registration.
Kotlin

@Configuration
class MyConfig {
    @Autowired
    fun setHandlerMapping(mapping: RequestMappingHandlerMapping, handler: UserHandler) { 
        val info = RequestMappingInfo.paths("/user/{id}").methods(RequestMethod.GET).build() 
        val method = UserHandler::class.java.getMethod("getUser", Long::class.java) 
        mapping.registerMapping(info, handler, method) 
    }
}
  1. Inject the target handler and handler mapping for controllers.
  2. Prepare the request mapping metadata.
  3. Get the handler method.
  4. Add registration.