Spring MVC 3.1中的@RequestMapping方法的新支持类
Spring 3.1分别为@RequestMapping方法引入了一组新的支持类,分别叫做RequestMappingHandlerMapping和RequestMappingHandlerAdapter。它们被推荐使用,甚至需要利用Spring MVC 3.1中的新功能和未来。默认情况下,MVC命名空间和MVC Java配置启用新的支持类,但是如果不使用,则必须显式配置。本节介绍旧支持类和新支持类之间的一些重要区别。
在Spring 3.1之前,类型和方法级请求映射在两个单独的阶段进行了检查 – 首先由DefaultAnnotationHandlerMapping选择一个控制器,并且实际的调用方法被AnnotationMethodHandlerAdapter缩小。
使用Spring 3.1中的新支持类,RequestMappingHandlerMapping是唯一可以决定哪个方法应该处理请求的地方。将控制器方法作为从类型和方法级@RequestMapping信息派生的每个方法的映射的唯一端点的集合。
这使得一些新的可能性。一旦HandlerInterceptor或HandlerExceptionResolver现在可以期望基于对象的处理程序是HandlerMethod,它允许它们检查确切的方法,其参数和关联的注释。 URL的处理不再需要跨不同的控制器进行拆分。
还有下面几件事情已经不复存在了:
- 首先使用SimpleUrlHandlerMapping或BeanNameUrlHandlerMapping选择控制器,然后基于@RequestMapping注释来缩小方法。
- 依赖于方法名称作为一种落后机制,以消除两个@RequestMapping方法之间的差异,这两个方法没有明确的路径映射URL路径, 通过HTTP方法。 在新的支持类中,@RequestMapping方法必须被唯一地映射。
- 如果没有其他控制器方法更具体地匹配,请使用单个默认方法(无显式路径映射)处理请求。 在新的支持类中,如果找不到匹配方法,则会引发404错误。
上述功能仍然支持现有的支持类。 不过要利用新的Spring MVC 3.1功能,您需要使用新的支持类。
URI 模版模式
可以使用URI模板方便地访问@RequestMapping方法中URL的所选部分。
URI模板是一个类似URI的字符串,包含一个或多个变量名称。 当您替换这些变量的值时,模板将成为一个URI。 所提出的RFC模板RFC定义了URI如何参数化。 例如,URI模板http://www.example.com/users/{userId}包含变量userId。 将fred的值分配给变量会得到http://www.example.com/users/fred。
在Spring MVC中,您可以使用方法参数上的@PathVariable注释将其绑定到URI模板变量的值:
@GetMapping("/owners/{ownerId}") public String findOwner(@PathVariable String ownerId, Model model) { Owner owner = ownerService.findOwner(ownerId); model.addAttribute("owner", owner); return "displayOwner"; } URI模板“/ owners / {ownerId}”指定变量名ownerId。 当控制器处理此请求时,ownerId的值将设置为在URI的适当部分中找到的值。 例如,当/ owner / fred出现请求时,ownerId的值为fred。
@GetMapping("/owners/{ownerId}") public String findOwner(@PathVariable("ownerId") String theOwner, Model model) { // implementation omitted }
或者如果URI模板变量名称与方法参数名称匹配,则可以省略该详细信息。 只要您的代码使用调试信息或Java 8上的参数编译器标记进行编译,Spring MVC将将方法参数名称与URI模板变量名称相匹配:
@GetMapping("/owners/{ownerId}") public String findOwner(@PathVariable String ownerId, Model model) { // implementation omitted } 一个方法能够有任意数量的
@PathVariable注解:
@GetMapping("/owners/{ownerId}/pets/{petId}") public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { Owner owner = ownerService.findOwner(ownerId); Pet pet = owner.getPet(petId); model.addAttribute("pet", pet); return "displayPet"; }
当在Map <String,String>参数上使用@PathVariable注释时,映射将填充所有URI模板变量。 URI模板可以从类型和方法级别@RequestMapping注释中进行组合。 因此,可以使用/ owner / 42 / pets / 21等URL调用findPet()方法。
@Controller @RequestMapping("/owners/{ownerId}") public class RelativePathUriTemplateController { @RequestMapping("/pets/{petId}") public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { // implementation omitted } } 一个@PathVariable参数可以是任何简单的类型,如int,long,Date等。如果没有这样做,Spring将自动转换为适当的类型或者抛出一个TypeMismatchException。 您还可以注册解析其他数据类型的支持。. See the section called “Method Parameters And Type Conversion” 和the section called “Customizing WebDataBinder initialization”.
具有正则表达式的URI模板模式
有时您需要更精确地定义URI模板变量。 考虑URL“/spring-web/spring-web-3.0.5.jar”。 你怎么把它分解成多个部分?
@RequestMapping注释支持在URI模板变量中使用正则表达式。 语法是{varName:regex},其中第一部分定义了变量名,第二部分定义了正则表达式。 例如:
@RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{extension:\\.[a-z]+}") public void handle(@PathVariable String version, @PathVariable String extension) { // ... }
路径模式
除了URI模板之外,@RequestMapping注释和所有组合的@RequestMapping变体也支持Ant样式的路径模式(例如/myPath/*.do)。 还支持URI模板变量和Ant-style glob的组合(例如/ owners / * / pets / {petId})。
路径模式比较
当URL匹配多个模式时,使用排序来查找最具体的匹配。
具有较低数量URI变量和通配符的模式被认为更具体。 例如/hotels/ {hotel} / *具有1个URI变量和1个通配符,被认为比/hotels/ {hotel} / **更具体,其中1个URI变量和2个通配符。
如果两个模式具有相同的计数,那么较长的模式被认为更具体。 例如/ foo / bar *比较长,被认为比/ foo / *更具体。
当两个模式具有相同的计数和长度时,具有较少通配符的模式被认为更具体。 例如/hotels/ {hotel}比/hotels/ *更具体。
下面有些额外增加的特殊的规则:
- 默认映射模式/ **比任何其他模式都要小。 例如/ api / {a} / {b} / {c}更具体。
- 诸如/ public / **之类的前缀模式比不包含双通配符的任何其他模式都不那么具体。 例如/ public / path3 / {a} / {b} / {c}更具体。
For 有关详细信息,请参阅AntPathMatcher中的AntPatternComparator。 请注意,可以自定义PathMatcher(参见Section 18.16.11, “Path Matching” ).
具有占位符的路径模式
@RequestMapping注释中的模式支持对本地属性和/或系统属性和环境变量的$ {…}占位符。 在将控制器映射到的路径可能需要通过配置进行定制的情况下,这可能是有用的。 有关占位符的更多信息,请参阅PropertyPlaceholderConfigurer类的javadocs。
后缀模式匹配
默认情况下,Spring MVC执行“。*”后缀模式匹配,以便映射到/ person的控制器也隐式映射到/person.*。这使得通过URL路径(例如/person.pdf,/person.xml)可以轻松地请求资源的不同表示。
后缀模式匹配可以关闭或限制为一组明确注册用于内容协商的路径扩展。通常建议通过诸如/ person / {id}之类的常见请求映射来减少歧义,其中点可能不表示文件扩展名,例如/person/joe@email.com vs /person/joe@email.com.json。此外,如下面的说明中所解释的,后缀模式匹配以及内容协商可能在某些情况下用于尝试恶意攻击,并且有充分的理由有意义地限制它们。
有关后缀模式匹配配置,请参见第18.16.11节“路径匹配”,内容协商配置第18.16.6节“内容协商”。
后缀模式匹配和RFD
反思文件下载(RFD)攻击是由Trustwave在2014年的一篇论文中首次描述的。攻击类似于XSS,因为它依赖于响应中反映的输入(例如查询参数,URI变量)。然而,不是将JavaScript插入到HTML中,如果基于文件扩展名(例如.bat,.cmd)双击,则RFD攻击依赖于浏览器切换来执行下载并将响应视为可执行脚本。
在Spring MVC @ResponseBody和ResponseEntity方法存在风险,因为它们可以呈现客户端可以通过URL路径扩展请求的不同内容类型。但是请注意,单独禁用后缀模式匹配或禁用仅用于内容协商的路径扩展都可以有效地防止RFD攻击。
为了全面保护RFD,在呈现响应体之前,Spring MVC添加了Content-Disposition:inline; filename = f.txt头来建议一个固定和安全的下载文件。只有当URL路径包含既不是白名单的文件扩展名,也没有明确注册用于内容协商的目的,这是完成的。但是,当URL直接输入浏览器时,可能会产生副作用。
许多常见的路径扩展名默认为白名单。此外,REST API调用通常不是直接用于浏览器中的URL。然而,使用自定义HttpMessageConverter实现的应用程序可以明确地注册用于内容协商的文件扩展名,并且不会为此类扩展添加Content-Disposition头。见第18.16.6节“Content Negotiation”。
这是CVE-2015-5211工作的一部分。 以下是报告中的其他建议:
- 编码而不是转义JSON响应。 这也是OWASP XSS的建议。 有关Spring的例子,请参阅spring-jackson-owasp.
- 将后缀模式匹配配置为关闭或仅限于明确注册的后缀
- 配置使用属性“useJaf”和“ignoreUnknownPathExtensions”设置为false的内容协商,这将导致具有未知扩展名的URL的406响应。 但是请注意,如果URL自然希望有一个结束点,这可能不是一个选择。
- 添加X-Content-Type-Options:nosniff头到响应。 Spring Security 4默认情况下执行此操作。
矩阵变量
URI规范RFC 3986定义了在路径段中包含名称 – 值对的可能性。规格中没有使用具体术语。可以应用一般的“URI路径参数”,尽管来自Tim Berners-Lee的旧帖子的更独特的“Matrix URI”也经常被使用并且是相当熟知的。在Spring MVC中,这些被称为矩阵变量。
矩阵变量可以出现在任何路径段中,每个矩阵变量用“;”分隔(分号)。例如:“/ cars; color = red; year = 2012”。多个值可以是“,”(逗号)分隔“color = red,green,blue”,或者变量名称可以重复“color = red; color = green; color = blue”。
如果URL预期包含矩阵变量,则请求映射模式必须使用URI模板来表示它们。这确保了请求可以正确匹配,无论矩阵变量是否存在,以及它们提供什么顺序。
以下是提取矩阵变量“q”的示例:
// 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 } 所有矩阵变量可以在Map中获得:
// 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" : 11, "s" : 23] } 请注意,为了使用矩阵变量,您必须将RequestMappingHandlerMapping的removeSemicolonContent属性设置为false。 默认设置为true。 MVC Java配置和MVC命名空间都提供了使用矩阵变量的选项。 如果您使用Java配置,使用MVC Java Config的高级自定义部分将介绍如何自定义RequestMappingHandlerMapping。 在MVC命名空间中,<mvc:annotation-driven>元素具有一个应该设置为true的enable-matrix-variables属性。 默认情况下设置为false。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <mvc:annotation-driven enable-matrix-variables="true"/> </beans>