Original address: https://www.codemore.top/cates/Backend/post/2018-04-10/spring-mvc-controller
declare Controller
Controller is also a standard Spring bean that can be WebApplicationContext
defined in Servlet. You can also use @Controller
annotations, and Spring will scan the annotations and automatically register them as Spring beans. Beans with automatic registration @Controller
annotations enabled can use the following Java Config configuration:
@Configuration
@ComponentScan("org.example.web")
public class WebConfig { // ... }
If you use xml configuration, as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.example.web"/> <!-- ... --> </beans>
request mapping
@RequestMapping
Requests can be mapped to specific Controller methods. Map requests by finding matching urls, http methods, request parameters, headers, media types. This annotation can be used both at the class level and at the method level. For convenience @RequestMapping
, the following shortcut annotations are provided according to different HTTP methods:
- @GetMapping
- @PostMapping
- @DeleteMapping
- @PutMapping
- @PatchMapping
An example is shown below:
@RestController
@RequestMapping("/persons")
class PersonController { @GetMapping("/{id}") public Person getPerson(@PathVariable Long id) { // ... } @PostMapping @ResponseStatus(HttpStatus.CREATED) public void add(@RequestBody Person person) { // ... } }
URI schematic
Request mapping supports glob patterns and wildcards
?
matches a character*
matches 0 or more characters**
Matching zero or more paths can be done by@PathVariable
accessing variables defined in the URI:
@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) { // ... }
URI variables can be defined in classes and methods:
@Controller
@RequestMapping("/owners/{ownerId}")
public class OwnerController { @GetMapping("/pets/{petId}") public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) { // ... } }
URI variables are automatically type-converted, and TypeMismatchException
an exception is thrown if it fails. Types such as , , , etc. are supported by default int
, long
and Date
other types that need to be supported can also be registered through DataBinder and Type Conversion. URI variable names can also be explicitly supported, for example @PathVariable("customId")
, but if compiled with debug information, or for Java 8 use -parameters
compilation, explicit names may not be required. The syntax {varName:regex}
indicates that the variable is matched according to the regular expression, for example "/spring-web-3.0.5.jar" can be matched using the following expression
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
public void handle(@PathVariable String version, @PathVariable String ext) { // ... }
URIs can also have embedded ${}
placeholders that PropertyPlaceHolderConfigurer
are parsed from local, system, environment variables or other configuration when the application starts. Spring MVC uses Spring core AntPathMatcher
to match paths.
Mode comparison
When there are many pattern matching URIs, comparisons must be made to find the most suitable match. This is achieved through AntPathMatcher.getPatternComparator(String path)
. URLs can be scored according to the number of variables and wildcards in the URI. If a URI has fewer variables and more wildcards, the score will be lower. When the scores of the matched patterns are the same, the one with the longer matching pattern is selected. If the scores and lengths are the same, the one with fewer variables than the wildcard is selected. /**
is not involved in grading and is always the last option. It is /plublic/**
also selected when no other pattern without two wildcards is matched. For more detailed information, you can check AntPathMatcher
in AntPatternComparator
. It can also be inherited PathMatcher
to customize URI matching.
suffix match
Spring MVC starts the .*
suffix matching mode by default, so that /person
the mapped controller can also be mapped to /person.*
. Extensions can be used in place of headers to Accept
indicate the type returned by the request. For example person.pdf
, person.xml
etc. This was intentional because browser Accept
headers were hard to parse in the past, but now browsers are Accept
more explicit, so the better option is to use Accept
. And in the past, there have been various problems when using suffix name matching. When using URI variables, path parameters, and URI encoding, the suffix pattern will lead to ambiguity. Postfix mode can be turned off using:
PathMatchConfigurer
ofuseSuffixPatternMatching(false)
ContentNeogiationConfigurer
offavorPathExtension(false)
Suffix matching and RFD
Reflected File Download (RFD) attacks are very similar to XSS attacks. XSS relies on the input of the request, such as query parameters, URI variables, etc., while RFD is that when the user clicks on the URL, the browser will download a malicious file, and after the user clicks, the host will be attacked. Since Spring MVC's @ResponseBody
sum ResponseEntity
renders different types of response content based on the URI suffix, it may be vulnerable to RFD attacks. Turning off suffix matching can reduce the risk of attacks, but cannot completely prevent RFD attacks. Content-Disposition:inline;filename=f.txt
To prevent RFD attacks, it is possible to add a secure download file when rendering the response content . Most extensions have a whitelist by default, and can HttpMessageConverter
register extensions for content negotiation via inheritance, which can avoid adding them in the response Content-Disposition
.
Consumable Media Type
Content-Type
The matching scope of the request can be narrowed by request, for example:
@PostMapping(path = "/pets", consumes = "application/json")
public void addPet(@RequestBody Pet pet) { // ... }
consumes also supports the negation of expressions, for example , it can !text/plain
refer to anything except text/plain
. You can define a class-level consumes, and its methods share the consumes. Unlike other @ReqeustMapping
attributes, the method's consumes will override the class definition.
Generated media types
Accept
The matching scope of the request can be narrowed by the header, for example:
@GetMapping(path = "/pets/{petId}", produces = "application/json;charset=UTF-8")
@ResponseBody
public Pet getPet(@PathVariable String petId) { // ... }
A media type can specify a character set. Negation of expressions is also supported, for example: !text/plain
refers to all except text/plain
. Like consumers, it is also possible to specify a class-level produces whose method properties also override class properties.
Parameters and HTTP headers
The range of matching requests can be narrowed by parameters. You can set whether there is a parameter ("myParam"), and vice versa ("!myParam") or specify a value ("myParam=myValue").
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue")
public void findPet(@PathVariable String petId) { // ... }
The same is true for HTTP headers
@GetMapping(path = "/pets", headers = "myHeader=myValue")
public void findPet(@PathVariable String petId) { // ... }