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:
@RestController
@RequestMapping("/persons")
class PersonController {
@GetMapping("/{id}")
public Person getPerson(@PathVariable Long id) {
// ...
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
}
}
@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 aPathContainer
. 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:
@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
// ...
}
@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:
@Controller
@RequestMapping("/owners/{ownerId}")
public class OwnerController {
@GetMapping("/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
// ...
}
}
@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:
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
public void handle(@PathVariable String name, @PathVariable String version, @PathVariable String ext) {
// ...
}
@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.txt
header 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:
@PostMapping(path = "/pets", consumes = "application/json")
public void addPet( @RequestBody Pet pet) {
// ...
}
- Using the
consumes
attribute to narrow the display range by content type.
@PostMapping("/pets", consumes = ["application/json" ])
fun addPet(@RequestBody pet: Pet) {
// ...
}
- 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:
@GetMapping(path = "/pets/{petId}", produces = "application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId) {
// ...
}
- Use the
produces
attribute to narrow the display range by content type.
@GetMapping("/pets/{petId}", produces = ["application/json"])
@ResponseBody
fun getPet( @PathVariable petId: String): Pet {
// ...
}
- 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:
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue ")
public void findPet(@PathVariable String petId) {
// ...
}
- Checks whether
myParam
is equal tomyValue
.
@GetMapping("/pets/{petId}", params = ["myParam=myValue"])
fun findPet(@PathVariable petId: String) {
// ...
}
- Checks whether
myParam
is equal to the value ofmyValue
.
The same can be used with request header conditions , as shown in the following example:
@GetMapping(path = "/pets", headers = "myHeader=myValue")
public void findPet(@PathVariable String petId) {
// ...
}
- Checks whether
myHeader
is equal tomyValue
.
@GetMapping("/pets", headers = ["myHeader=myValue"])
fun findPet( @PathVariable petId: String) {
// ...
}
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:
@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);
}
}
- Inject the target handler and handler mapping for controllers.
- Prepare the request mapping metadata.
- Get the handler method.
- Add registration.
@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)
}
}
- Inject the target handler and handler mapping for controllers.
- Prepare the request mapping metadata.
- Get the handler method.
- Add registration.
GO TO FULL VERSION