1.1 DispatcherServlet
1.2 Filters
1.3 注解控制器-@Controller、@RestController
Spring MVC提供基于注释的编程模型,组件使用**@Controller**、 @RestController注解来表达请求映射,请求输入,异常处理等。带注释的控制器具有灵活的方法签名,不必扩展基类,也不必实现特定的接口。如下:
@Controller
public class HelloController {
@GetMapping("/hello")
public String handle(Model model) {
model.addAttribute("message", "Hello World!");
return "index";
}
}
上面的实例,方法接受一个Model并将视图名称作为String返回。
1.3.1 描述
@Controller允许自动检测,使用Spring普遍支持检测对准@Component在类路径中类和自动注册bean定义他们。它还充当带注释的类的构造型,表明它作为Web组件的角色。
要启用此类@Controller的自动检测,可以将 @componentScan 组件扫描添加到Java配置中,如以下示例所示:
@Configuration
@ComponentScan("org.example.web")
public class WebConfig {
// ...
}
以下实例XML配置等效于上面Java配置:
<?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>
@RestController是一个组合注释,它本身是元注释的,
@Controller和**@ResponseBody**指示一个控制器,其每个方法都继承了类型级@ResponseBody注释。因此,响应主体直接写入视图解析器和使用HTML模板呈现。
AOP代理
在某些情况下,您需要在运行时使用AOP代理装饰控制器。例如,如果您选择@Transactional注解直接标注控制器。在这种情况下,对于控制器而言,我们建议使用基于类的代理。这通常是控制器的默认选择。但是,如果一个控制器必须实现一个接口,这不是一个Spring上下文回调(例如InitializingBean,*Aware和其他人),您可能需要显式配置基于类的代理。例如,tx:annotation-driven/\可以更改为<tx:annotation-driven proxy-target-class=“true”/>。
1.3.2 请求映射-@RequestMapping
使用**@RequestMapping**注释将请求映射到控制器方法。它具有各种属性,可通过URL,HTTP方法,请求参数,标头和媒体类型进行匹配。您可以在类级别使用它来表示共享映射,或者在方法级别使用它来缩小到特定的端点映射。
还有HTTP方法特定的快捷方式变体@RequestMapping:
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
- @PatchMapping
类和方法级别请求映射示例:
@RestController
@RequestMapping("/persons")
class PersonController {
@GetMapping("/{id}")
public Person getPerson(@PathVariable Long id) {
// ...
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
}
}
URI模式
可以使用以下全局模式和通配符映射请求:
- ?:匹配一个字符
- *:匹配路径中0个或多个字符
- **:匹配0个或多个路径段
还可以使用声明URI变量并访问它们的值**@PathVariable**,如:
@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
// ...
}
可以在类和方法级别声明URI变量,如:
@Controller
@RequestMapping("/owners/{ownerId}")
public class OwnerController {
@GetMapping("/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
// ...
}
}
URI变量会自动转换为适当的类型,或者TypeMismatchException 被引发。简单类型(int,long,Date,等)默认支持,你可以注册任何其它数据类型的支持。
可以显式命名URI变量(例如,@PathVariable(“customId”)),但是如果名称相同并且您的代码使用调试信息或-parametersJava 8上的编译器标志进行编译,则可以保留该详细信息。
语法{varName:regex}声明一个URI变量,其正则表达式的语法为{varName:regex}。例如,给定URL “/spring-web-3.0.5 .jar”,以下方法提取名称,版本和文件扩展名:
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
public void handle(@PathVariable String version, @PathVariable String ext) {
// ...
}
URI路径模式还可以具有嵌入式${…}占位符,这些占位符在启动时通过使用PropertyPlaceHolderConfigurer针对本地,系统,环境和其他属性源来解析。例如,您可以使用此参数来基于某些外部配置参数化基本URL。
Spring MVC使用URI路径匹配的PathMatcher和AntPathMatcher实现 在spring-core模块。
模式比较
当多个模式与URL匹配时,必须对它们进行比较以找到最佳匹配。这是通过使用**AntPathMatcher.getPatternComparator(String path)**来完成的,它会查找更具体的模式。
后缀匹配-xxx.*
默认情况下,Spring MVC执行.后缀模式匹配,以便映射到的控制器/person也隐式映射到/person.。然后,将文件的扩展名是用于解释所请求的内容类型以用于响应(也就是,代替Accept标题) -例如,/person.pdf, /person.xml,和其他。
当浏览器用于发送Accept难以一致解释的标头时,必须以这种方式使用文件扩展名。目前,这不再是必需品,使用Accept标题应该是首选。
要完全禁用文件扩展名,必须同时设置以下两项:
- useSuffixPatternMatching(false)
- favorPathExtension(false)
基于URL的内容协商仍然有用(例如,在浏览器中键入URL时)。为此,我们建议使用基于查询参数的策略来避免文件扩展名带来的大多数问题。或者,如果必须使用文件扩展名,请考虑通过ContentNegotiationConfigurer的mediaTypes属性将它们限制为显式注册的扩展名列表 。
后缀匹配、RFD(反射文件下载)攻击
反射文件下载(RFD)攻击类似于XSS,因为它依赖于响应中反映的请求输入(例如,查询参数和URI变量)。但是,RFD攻击不依赖于将JavaScript插入HTML,而是依赖浏览器切换来执行下载,并在以后双击时将响应视为可执行脚本。
在Spring MVC中,@ResponseBody和ResponseEntity方法是有风险的,因为它们可以呈现不同的内容类型,客户端可以通过URL路径扩展要求。禁用后缀模式匹配并使用路径扩展进行内容协商可降低风险,但不足以防止RFD攻击。
防止RFD攻击: 在呈现响应主体之前,Spring MVC添加了一个 Content-Disposition:inline;filename=f.txt标题来建议一个固定且安全的下载文件。仅当URL路径包含既未列入白名单也未明确注册用于内容协商的文件扩展名时,才会执行此操作。但是,当直接在浏览器中输入URL时,它可能会产生副作用。
消费者类型
可以根据请求缩小请求映射范围Content-Type,如:
@PostMapping(path = "/pets", consumes = "application/json")
public void addPet(@RequestBody Pet pet) {
// ...
}
使用consumes属性通过内容类型缩小映射范围。
该consumes属性还支持否定表达式 。如:
- !text/plain:表示除了text/plain以外的任何内容类型。
consumes在类级别声明共享属性。与大多数其他请求映射属性不同,在类级别使用时,方法级别consumes属性会覆盖类级别声明。
MediaType提供常用媒体类型的常量,如: APPLICATION_JSON_VALUE和APPLICATION_XML_VALUE
生产者媒体类型
可以根据Accept请求标头和控制器方法生成的内容类型列表缩小请求映射,如:
@GetMapping(path = "/pets/{petId}", produces = "application/json;charset=UTF-8")
@ResponseBody
public Pet getPet(@PathVariable String petId) {
// ...
}
使用**produces**属性通过内容类型缩小映射范围。
对于JSON内容类型,即使RFC7159 明确指出“没有为此注册定义charset参数”,也应指定UTF-8字符集 ,因为某些浏览器要求它正确解释UTF-8特殊字符。
produces属性可以在类级别声明共享属性。与大多数其他请求映射属性不同,在类级别使用时,方法级别produces属性会覆盖类级别声明。
标题、参数
可以根据请求参数条件缩小请求映射。
可以测试是否存在请求参数(myParam),缺少one(!myParam)或特定值(myParam=myValue)。
以下示例显示如何测试特定值:
//测试是否myParam等于myValue
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue")
public void findPet(@PathVariable String petId) {
// ...
}
还可以将其与请求标头条件一起使用,如以下示例所示:
//测试是否myHeader等于myValue。
@GetMapping(path = "/pets", headers = "myHeader=myValue")
public void findPet(@PathVariable String petId) {
// ...
}
HTTP HEAD,OPTIONS
@GetMapping(和@RequestMapping(method=HttpMethod.GET))透明地支持HTTP HEAD以进行请求映射。控制器方法无需更改。应用的响应包装器javax.servlet.http.HttpServlet确保将Content-Length 标头设置为写入的字节数(不实际写入响应)。
@GetMapping(和@RequestMapping(method=HttpMethod.GET))隐式映射到并支持HTTP HEAD。处理HTTP HEAD请求就好像它是HTTP GET一样,除了编写字节数而不是写入正文,并设置Content-Length 标头。
默认情况下,通过将Allow响应标头设置为@RequestMapping具有匹配URL模式的所有方法中列出的HTTP方法列表来处理HTTP OPTIONS 。
对于@RequestMapping没有HTTP方法声明,Allow标头设置为 GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS。控制器方法应该总是声明支持HTTP方法(例如,通过使用HTTP方法特定的变体: @GetMapping,@PostMapping,及其他)。
可以将@RequestMapping方法显式映射到HTTP HEAD和HTTP OPTIONS,但在常见情况下这不是必需的。
自定义注解
Spring MVC支持使用组合注释 进行请求映射。这些注释本身是元注释的, @RequestMapping并且用于@RequestMapping 以更窄,更具体的目的重新声明属性的子集(或全部)。
@GetMapping,@PostMapping,@PutMapping,@DeleteMapping,@PatchMapping提供它们是因为,大多数控制器方法应该映射到特定的HTTP方法而不是使用@RequestMapping,默认情况下,它与所有HTTP方法匹配。
Spring MVC还支持使用“自定义请求”匹配“自定义请求映射属性”。这是一个更高级的选项,需要子类化 RequestMappingHandlerMapping和覆盖getCustomMethodCondition方法,您可以在其中检查自定义属性并返回自己的属性RequestCondition。
明确注册
可以以编程方式注册处理程序方法,可以将其用于动态注册或高级情况,例如不同URL下的同一处理程序的不同实例。以下示例注册处理程序方法:
@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.3.3 处理程序方法
@RequestMapping 处理程序方法具有灵活的签名,可以从一系列受支持的控制器方法参数和返回值中进行选择。
JDK 8的java.util.Optional被支撑作为组合的方法的参数与具有注解required的属性(例如,@RequestParam,@RequestHeader,和其它物质)和相当于required=false。
方法参数:
下表描述了受支持的控制器方法参数。任何参数都不支持反应类型。
控制器方法参数 | 说明 |
---|---|
WebRequest, NativeWebRequest | 无需直接使用Servlet API即可访问请求参数以及请求和会话属性。 |
javax.servlet.ServletRequest, javax.servlet.ServletResponse | 选择任何特定的请求或响应类型-例如ServletRequest,HttpServletRequest或春天的MultipartRequest,MultipartHttpServletRequest。 |
javax.servlet.http.HttpSession | 强制进行会话。因此,这种论点永远不会null。请注意,会话访问不是线程安全的。如果允许多个请求同时访问会话,请考虑将RequestMappingHandlerAdapter实例的synchronizeOnSession标志设置为 true。 |
javax.servlet.http.PushBuilder | 用于编程HTTP / 2资源推送的Servlet 4.0推送构建器API。请注意,根据Servlet规范,PushBuilder如果客户端不支持该HTTP / 2功能,则注入的实例可以为null。 |
java.security.Principal | 当前经过身份验证的用户 - Principal如果已知,可能是特定的实现类 |
HttpMethod | 请求的HTTP方法。 |
java.util.Locale | 当前请求区域设置,由最具体确定LocaleResolver可用的(实际上,配置的LocaleResolver或LocaleContextResolver)。 |
java.util.TimeZone + java.time.ZoneId | 与当前请求关联的时区,由a确定LocaleContextResolver。 |
java.io.InputStream, java.io.Reader | 用于访问Servlet API公开的原始请求主体 |
java.io.OutputStream, java.io.Writer | 用于访问Servlet API公开的原始响应主体。 |
@PathVariable | 用于访问URI模板变量 |
@MatrixVariable | 用于访问URI路径段中的名称 - 值对。 |
@RequestParam | 用于访问Servlet请求参数,包括多部分文件。参数值将转换为声明的方法参数类型。见@RequestParam以及多部分。请注意,@RequestParam对于简单参数值,使用是可选的。 |
@RequestHeader | 用于访问请求标头。标头值将转换为声明的方法参数类型。 |
@CookieValue | 用于访问cookie。Cookie值将转换为声明的方法参数类型。 |
@RequestBody | 用于访问HTTP请求正文。通过使用HttpMessageConverter实现将正文内容转换为声明的方法参数类型。 |
HttpEntity | 用于访问请求标头和正文。身体转换为HttpMessageConverter。 |
@RequestPart | 要访问multipart/form-data请求中的某个部分,请使用。转换部件的主体HttpMessageConverter。 |
java.util.Map,org.springframework.ui.Model,org.springframework.ui.ModelMap | 用于访问HTML控制器中使用的模型,并将其作为视图呈现的一部分暴露给模板。 |
RedirectAttributes | 指定在重定向(即,要附加到查询字符串)的情况下使用的属性,以及临时存储的flash属性,直到重定向后的请求为止。 |
@ModelAttribute | 用于访问模型中的现有属性(如果不存在则实例化),并应用数据绑定和验证。见@ModelAttribute以及 型号和DataBinder。请注意,使用@ModelAttribute是可选的(例如,设置其属性)。 |
Errors, BindingResult | 用于访问命令对象的验证和数据绑定(即@ModelAttribute参数)中的错误或来自@RequestBody或 @RequestPart参数验证的错误。您必须在经过验证的方法参数之后立即声明一个Errors或BindingResult参数。 |
SessionStatus + class-level @SessionAttributes | 用于标记表单处理完成,它触发通过类级别@SessionAttributes注释声明的会话属性的清除。 |
UriComponentsBuilder | 用于准备相对于当前请求的主机,端口,方案,上下文路径和servlet映射的文字部分的URL。 |
@SessionAttribute | 用于访问任何会话属性,与由于类级@SessionAttributes声明而存储在会话中的模型属性相反 |
@RequestAttribute | 用于访问请求属性。 |
其它参数 | 如果方法参数与此表中的任何早期值不匹配,并且它是一个简单类型(由BeanUtils#isSimpleProperty确定 ,则它被解析为a @RequestParam。否则,它将被解析为a @ModelAttribute。 |
返回值:
下表描述了支持的控制器方法返回值。所有返回值都支持反应类型。
类型转换:
一些注解的控制器方法的参数以String数据类型为基础的请求输入(如 @RequestParam,@RequestHeader,@PathVariable,@MatrixVariable,@CookieValue)可以要求转换为其他类型。
对于此类情况,将根据配置的转换器自动应用类型转换。默认情况下,简单的类型(int,long,Date)支持。可以通过自定义类型转换WebDataBinder(或者通过注册 Formatters与FormattingConversionService。
矩阵变量-即一个变量多个值
矩阵变量可以出现在任何路径段中,每个变量用分号分隔,多个值用逗号分隔(例如/cars;color=red,green;year=2012)。也可以通过重复的变量名称指定多个值(例如, color=red;color=green;color=blue)。
如果URL预计包含矩阵变量,则控制器方法的请求映射必须使用URI变量来屏蔽该变量内容,并确保请求可以成功匹配,而与矩阵变量顺序和存在无关。以下示例使用矩阵变量:
// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {
// petId == 42
// q == 11
}
鉴于所有路径段都可能包含矩阵变量,可能需要消除矩阵变量预期所在的路径变量的歧义。以下示例说明了如何执行此操作:
// GET /owners/42;q=11/pets/21;q=22
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable(name="q", pathVar="ownerId") int q1,
@MatrixVariable(name="q", pathVar="petId") int q2) {
// q1 == 11
// q2 == 22
}
矩阵变量可以定义为可选,并指定默认值,如以下示例所示:
// GET /pets/42
@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {
// q == 1
}
要获取所有矩阵变量,可以使用MultiValueMap,如以下示例所示:
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable MultiValueMap<String, String> matrixVars,
@MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) {
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 22, "s" : 23]
}
启用矩阵变量的使用:
- 在MVC基于Java配置,你需要设置一个UrlPathHelper与removeSemicolonContent=false通过 路径匹配。
- 在MVC基于XML配置命名空间中,您可以设置 <mvc:annotation-driven enable-matrix-variables=“true”/>
@RequestParam-即请求参数
可以使用@RequestParam注解将Servlet请求参数(即查询参数或表单数据)绑定到控制器中的方法参数。如下:
@Controller
@RequestMapping("/pets")
public class EditPetForm {
// ...
//使用@RequestParam绑定petId。
@GetMapping
public String setupForm(@RequestParam("petId") int petId, Model model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet);
return "petForm";
}
// ...
}
默认情况下,需要使用此注解的方法参数,但可以通过将@RequestParam注解的required标志设置为 false或通过使用java.util.Optional包装器声明参数来指定方法参数是可选的。
如果不是目标方法参数类型,则会自动应用类型转换 String。
将参数类型声明为数组或列表允许为同一参数名称解析多个参数值。
若@RequestParam注解声明为Map<String, String>或 MultiValueMap<String, String>没有在中指注解指定的参数名称,则使用每个给定参数名称的请求参数值填充映射。
使用@RequestParam是可选的(例如,设置其属性)。默认情况下,任何属于简单值类型的参数(由BeanUtils#isSimpleProperty确定 )并且未被任何其他参数解析器解析,都被视为使用注释@RequestParam
@RequestHeader-即请求头
使用@RequestHeader注解将请求头绑定到控制器中的方法参数。
以下示例获取Accept-Encoding和Keep-Alive标头的值:
@GetMapping("/demo")
public void handle(
// 获取Accept-Encoding标题的值
@RequestHeader("Accept-Encoding") String encoding,
//获取Keep-Alive标题的值
@RequestHeader("Keep-Alive") long keepAlive) {
//...
}
若不是目标方法参数类型 String,则自动应用类型转换。
当@RequestHeader注解上的使用Map<String, String>, MultiValueMap<String, String>或HttpHeaders参数,则Map被填充有所有标头值。
@CookieValue
使用@CookieValue注释将HTTP cookie的值绑定到控制器中的方法参数。
获取cookie 的JSESSIONID的价值:
@GetMapping("/demo")
public void handle(@CookieValue("JSESSIONID") String cookie) {
//...
}
@ModelAttribute
@SessionAttributes
@SessionAttribute
@RequestAttribute
重定向属性
Flash属性
Multipart-多部分
@RequestBody
HttpEntity
@ResponseBody
ResponseEntity
Jackson
1.3.4 Model模型
可以使用@ModelAttribute注解:
- 在方法中的方法参数,@RequestMapping用于Object从模型创建或访问模型并通过WebDataBinder将其绑定到请求
- 作为方法级注释@Controller或@ControllerAdvice类,有助于在任何@RequestMapping方法调用之前初始化模型
- 在@RequestMapping标记其返回值的方法上是模型属性