|
对于最新稳定版本,请使用Spring Framework 7.0.1! |
映射请求
本节讨论注释控制器的请求映射。
@RequestMapping
你可以使用@RequestMapping注释用于映射请求到控制器的方法。它已经
通过 URL、HTTP 方法、请求参数、头部和媒体匹配的各种属性
类型。你可以在类层面用它来表达共享映射,也可以在方法层面使用
以便缩小到特定的端点映射。
还有 HTTP 方法专用的快捷方式变体@RequestMapping:
-
@GetMapping -
@PostMapping -
@PutMapping -
@DeleteMapping -
@PatchMapping
这些快捷方式是自定义注释,提供的原因是,
可以说,大多数控制器方法应映射到特定的HTTP方法,而不是
用@RequestMapping,默认情况下,它与所有HTTP方法匹配。
一个@RequestMapping在类级层面仍需表达共享映射。
@RequestMapping不能与其他词组一起使用@RequestMapping在同一元素(类、接口或方法)上声明的注释。如果
倍数@RequestMapping在同一元素上检测到注释,警告将是
被记录,且只使用最初的映射。这同样适用于作曲@RequestMapping注释如@GetMapping,@PostMapping等。 |
以下示例具有类型和方法层级映射:
-
Java
-
Kotlin
@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模式
@RequestMapping方法可以通过URL模式进行映射。有两种替代方案:
-
路径模式—— 一个预解析的模式,与同样预解析为 的 URL 路径匹配路径容器. 该方案专为网页应用设计,能够有效处理编码和路径参数,并高效匹配。 -
蚁径匹配器——将字符串模式与字符串路径匹配。这是最初的解决方案,也用于Spring配置中用于选择类路径上的资源,文件系统及其他位置。这种方法效率较低,字符串路径输入是有效处理编码及其他URL问题的挑战。
路径模式是Web应用推荐的解决方案,并且是Spring WebFlux 中唯一的选择。从 5.3 版本起,它被启用用于 Spring MVC,并且默认启用。从版本 6.0 开始。请参见 MVC 配置路径匹配选项的自定义。
路径模式支持与 相同的模式语法蚁径匹配器. 此外,它还支持捕获模式,例如:{*Spring},用于匹配0个或多个路径段位于一条路径的末端。路径模式同时限制了用于匹配多个路径段的使用,仅允许在模式结束时使用。这消除了许多在为特定请求选择最佳匹配模式时存在的歧义。完整的模式语法请参考 PathPattern 和 AntPathMatcher。**
一些示例模式:
-
“/资源/ima?e.png”- 匹配路径段中的一个字符 -
“/资源/*.png”- 匹配路径段中零个或多个字符 -
“/资源/**”- 匹配多条路径段 -
“/projects/{project}/versions”- 匹配路径段并将其捕获为变量 -
“/projects/{project:[a-z]+}/versions”- 用正则表达式匹配并捕获变量
捕获的URI变量可以通过以下方式访问@PathVariable. 例如:
-
Java
-
Kotlin
@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 {
// ...
}
你可以在类级和方法层级声明URI变量,如下示例所示:
-
Java
-
Kotlin
@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 变量会自动转换为相应类型,或者类型不匹配例外被提升。简单类型 (智力,长,日期,等等)默认支持,你可以注册支持任何其他数据类型。参见类型转换和DataBinder.
你可以显式地命名URI变量(例如,@PathVariable(“customId”)),但你可以如果名称相同且你的代码编译时是用-参数编译器标志。
语法{varName:regex}声明一个带有正则表达式的URI变量,使得语法{varName:regex}. 例如,给定URL。“/spring-web-3.0.5.jar”,以下方法提取名称、版本和文件扩展名:
-
Java
-
Kotlin
@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路径模式也可以嵌入${…}在启动时解决的占位符通过使用PropertySourcesPlaceholderConfigurer针对本地、系统、环境和其他属性源。例如,你可以利用它参数化基于某些外部配置的基础URL。
图案比较
当多个模式匹配一个URL时,必须选择最佳匹配。此过程包括根据是否使用解析,以下之一路径模式是否启用:
两者都有助于排序带有更具体特征的模式。当该模式的 URI 变量数较少(计为 1)、单万用符数(计为 1)、以及双万用符数(计为 2)较少。给定分数相等时,选择较长的模式。在相同得分和长度下,URI 变量多于万用符的模式为 选择。
默认映射模式()被排除在评分之外,并且总是最后排序。此外,前缀模式(例如/**/公共/**)被认为比没有双万能符的其他模式更不具备特殊性。
欲了解详细信息,请点击上述链接至模式比较器。
后缀匹配
从5.3版本开始,默认情况下Spring MVC不再执行.*后缀模式匹配控制器映射到/人也隐式映射为/人。*.因此,路径扩展不再用于解释
响应所需的内容类型——例如,/person.pdf,/person.xml,
诸如此类。
当浏览器发送文件时,这种方式使用文件扩展名是必要的接受头
这些内容很难一致地解读。目前,这已不再是必要,
使用接受页眉应该是首选。
随着时间推移,文件扩展名的使用在多种方面被证明存在问题。 当它与URI变量、路径参数和 URI编码。关于基于URL授权的推理 安全性(详见下一节)也会变得更加困难。
要完全禁用5.3之前版本中的路径扩展,请设置以下参数:
-
useSuffixPatternMatching(false),参见 PathMatchConfigurer -
favorPathExtension(false),参见 ContentNegotiationConfigurer
有一种方式可以请求除“接受”头部仍然可以
比如在浏览器输入URL时,会很有用。路径扩展的一个安全替代方案是
使用查询参数策略。如果必须使用文件扩展名,可以考虑限制
通过媒体类型ContentNegotiationConfigurer的财产。
后缀匹配与RFD
反射文件下载(RFD)攻击与XSS类似,它依赖于请求输入 (例如,查询参数和URI变量)反映在响应中。然而,代替 将JavaScript插入HTML时,RFD攻击依赖浏览器切换来执行 下载并把回复当作可执行脚本,之后双击时可以把它当作脚本处理。
春季MVC中,@ResponseBody和响应实体方法存在风险,因为
它们可以渲染不同类型的内容,客户端可以通过 URL 路径扩展来请求这些内容。
禁用后缀模式匹配并使用路径扩展进行内容协商
降低风险,但不足以防止RFD攻击。
为了防止RFD攻击,在渲染响应体之前,Spring MVC会添加一个内容-倾向:inline;filename=f.txt标题建议固定且安全的下载
文件。只有当 URL 路径包含既不是也不是的文件扩展名时,才会这样做
被允许为安全,也未明确注册内容协商。不过,它确实可以
当URL直接输入浏览器时,可能会有副作用。
许多常见路径扩展默认被允许为安全。自定义应用HttpMessage转换器实现可以显式注册内容的文件扩展名
协商以避免发生内容-处理为这些扩展添加了标题。
参见内容类型。
更多信息请参见CVE-2015-5211 与RFD相关的建议。
消耗性媒体类型
你可以根据内容类型关于请求,
如下示例所示:
-
Java
-
Kotlin
@PostMapping(path = "/pets", consumes = "application/json") (1)
public void addPet(@RequestBody Pet pet) {
// ...
}
| 1 | 使用消耗属性用于按内容类型缩小映射范围。 |
@PostMapping("/pets", consumes = ["application/json"]) (1)
fun addPet(@RequestBody pet: Pet) {
// ...
}
| 1 | 使用消耗属性用于按内容类型缩小映射范围。 |
这消耗属性还支持否定表达式——例如,!文本/平坦表示任意
内容类型除文本/纯文字.
你可以声明共享消耗属性在职业层面。与大多数其他国家不同
然而,请求映射属性在类级层面使用时,也就是方法层面消耗属性
覆盖而非扩展类级声明。
媒体类型为常用介质类型提供常量,例如APPLICATION_JSON_VALUE和APPLICATION_XML_VALUE. |
可制作媒体类型
你可以根据接受请求头和
以下示例展示了控制器方法产生的内容类型:
-
Java
-
Kotlin
@GetMapping(path = "/pets/{petId}", produces = "application/json") (1)
@ResponseBody
public Pet getPet(@PathVariable String petId) {
// ...
}
| 1 | 使用生产属性用于按内容类型缩小映射范围。 |
@GetMapping("/pets/{petId}", produces = ["application/json"]) (1)
@ResponseBody
fun getPet(@PathVariable petId: String): Pet {
// ...
}
| 1 | 使用生产属性用于按内容类型缩小映射范围。 |
媒体类型可以指定字符集。支持否定表达式——例如,!文本/平坦指除“文本/纯文字”以外的任何内容类型。
你可以声明共享生产属性在职业层面。与大多数其他国家不同
然而,请求映射属性在类级层面使用时,也就是方法层面生产属性
覆盖而非扩展类级声明。
媒体类型为常用介质类型提供常量,例如APPLICATION_JSON_VALUE和APPLICATION_XML_VALUE. |
参数,头部
你可以根据请求参数条件缩小请求映射范围。你可以检测
请求参数的存在(我的param),表示没有 (!我的帕拉姆),或为
比值(myParam=myValue).以下示例展示了如何检测特定值:
-
Java
-
Kotlin
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") (1)
public void findPet(@PathVariable String petId) {
// ...
}
| 1 | 测试我的param等于我的价值. |
@GetMapping("/pets/{petId}", params = ["myParam=myValue"]) (1)
fun findPet(@PathVariable petId: String) {
// ...
}
| 1 | 测试我的param等于我的价值. |
你也可以用同样的方法来处理请求头条件,如下示例所示:
-
Java
-
Kotlin
@GetMapping(path = "/pets/{petId}", headers = "myHeader=myValue") (1)
public void findPet(@PathVariable String petId) {
// ...
}
| 1 | 测试myHeader等于我的价值. |
@GetMapping("/pets/{petId}", headers = ["myHeader=myValue"]) (1)
fun findPet(@PathVariable petId: String) {
// ...
}
| 1 | 测试myHeader等于我的价值. |
HTTP 头部,选项
@GetMapping(和@RequestMapping(method=HttpMethod.GET)) 支持 HTTP HEAD
透明地用于请求映射。控制器的方法无需更改。
响应装装器,应用于jakarta.servlet.http.HttpServlet,确保了内容长度头部设置为写入的字节数(但未实际写入响应)。
默认情况下,HTTP OPTIONS 通过设置允许HTTP列表的响应头
所有方法均列出@RequestMapping具有匹配URL模式的方法。
对于一个@RequestMapping如果没有HTTP方法声明,则允许首部设置为获取、头部、发布、放置、补丁、删除、选项.控制器方法应始终声明
支持的HTTP方法(例如,通过使用HTTP方法的特定变体:@GetMapping,@PostMapping,以及其他。
你可以显式地映射@RequestMappingHTTP HEAD 和 HTTP OPTIONS 的 method ,但
在常见情况下并非必要。
自定义注释
Spring MVC 支持使用组合注释进行请求映射。这些注释本身也被元注释@RequestMapping并由 组成以重新声明 的子集(或全部)@RequestMapping属性具有更狭窄、更具体的目的。
@GetMapping,@PostMapping,@PutMapping,@DeleteMapping和@PatchMapping是
组成注释示例。他们之所以被提供,可以说是因为大多数
控制器方法应映射到特定的HTTP方法,而不是使用@RequestMapping,
默认情况下,它与所有HTTP方法匹配。如果你需要一个实现示例
写成注释,看看它们是如何声明的。
@RequestMapping不能与其他词组一起使用@RequestMapping在同一元素(类、接口或方法)上声明的注释。如果
倍数@RequestMapping在同一元素上检测到注释,警告将是
被记录,且只使用最初的映射。这同样适用于作曲@RequestMapping注释如@GetMapping,@PostMapping等。 |
Spring MVC 还支持自定义请求映射属性和自定义请求匹配
逻辑。这是一个更高级的选项,需要子职业化请求映射处理映射并且覆盖getCustomMethodCondition方法,其中
你可以检查自定义属性并返回自己的请求条件.
显式注册
你可以编程注册处理方法,这些方法可以用来进行动态处理 注册或高级情况,例如同一处理器的不同实例 在不同的网址下。以下示例注册了一个处理方法:
-
Java
-
Kotlin
@Configuration
public class MyConfig {
@Autowired
public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) (1)
throws NoSuchMethodException {
RequestMappingInfo info = RequestMappingInfo
.paths("/user/{id}").methods(RequestMethod.GET).build(); (2)
Method method = UserHandler.class.getMethod("getUser", Long.class); (3)
mapping.registerMapping(info, handler, method); (4)
}
}
| 1 | 注入目标处理器和控制器的处理器映射。 |
| 2 | 准备请求映射元数据。 |
| 3 | 学用牵引人的方法。 |
| 4 | 添加注册信息。 |
@Configuration
class MyConfig {
@Autowired
fun setHandlerMapping(mapping: RequestMappingHandlerMapping, handler: UserHandler) { (1)
val info = RequestMappingInfo.paths("/user/{id}").methods(RequestMethod.GET).build() (2)
val method = UserHandler::class.java.getMethod("getUser", Long::class.java) (3)
mapping.registerMapping(info, handler, method) (4)
}
}
| 1 | 注入目标处理器和控制器的处理器映射。 |
| 2 | 准备请求映射元数据。 |
| 3 | 学用牵引人的方法。 |
| 4 | 添加注册信息。 |
@HttpExchange
虽然主要目的是@HttpExchange是抽象HTTP客户端代码,其中
生成代理,是其上的HTTP 接口
此类注释是合同中立的,适用于客户端或服务器的使用。
除了简化客户端代码外,还有一些情况下 HTTP 接口
可能是服务器暴露API以便客户端访问的方便方式。这导致了
客户端与服务器之间的耦合度增加,通常不是一个好选择,
尤其是对公共 API 来说,但这可能正是内部 API 的目标。
这是春云中常用的方法,也是原因之一@HttpExchange是
作为替代方案支持的@RequestMapping用于服务器端处理
控制器类。
例如:
-
Java
-
Kotlin
@HttpExchange("/persons")
interface PersonService {
@GetExchange("/{id}")
Person getPerson(@PathVariable Long id);
@PostExchange
void add(@RequestBody Person person);
}
@RestController
class PersonController implements PersonService {
public Person getPerson(@PathVariable Long id) {
// ...
}
@ResponseStatus(HttpStatus.CREATED)
public void add(@RequestBody Person person) {
// ...
}
}
@HttpExchange("/persons")
interface PersonService {
@GetExchange("/{id}")
fun getPerson(@PathVariable id: Long): Person
@PostExchange
fun add(@RequestBody person: Person)
}
@RestController
class PersonController : PersonService {
override fun getPerson(@PathVariable id: Long): Person {
// ...
}
@ResponseStatus(HttpStatus.CREATED)
override fun add(@RequestBody person: Person) {
// ...
}
}
@HttpExchange和@RequestMapping有分歧。@RequestMapping可以通过路径模式、HTTP方法映射到任意数量的请求,
以及更多,同时@HttpExchange声明一个带有具体HTTP方法的单端点,
路径,以及内容类型。
对于方法参数和返回值,通常,@HttpExchange支撑
方法参数的子集@RequestMapping确实如此。值得注意的是,它排除了任何
服务器端特定的参数类型。详情请参见@HttpExchange和@RequestMapping列表。