映射请求 (Mapping Requests)
你可以使用 @RequestMapping 注解将请求映射到控制器方法。它具有各种属性,可通过 URL、HTTP 方法、请求参数、标头和媒体类型进行匹配。你可以在类级别使用它来表达共享映射,或在方法级别使用它来缩小到特定的端点映射。
快捷变体
常见的 HTTP 方法都有对应的快捷注解:
@GetMapping@PostMapping@PutMapping@DeleteMapping@PatchMapping
这些变体是组合注解,因为大多数控制器方法应该映射到特定的 HTTP 方法,而不是使用默认匹配所有方法的 @RequestMapping。类级别仍需使用 @RequestMapping 来表达共享路径。
URI 模式 (URI Patterns)
Spring MVC 使用 PathPattern 来匹配请求。
- Glob 模式:支持
?(匹配一个字符)、*(路径段内匹配)、**(匹配零或多个路径段)。 - 变量捕获:使用
{name}捕获路径变量,通过@PathVariable访问。 - 正则表达式:使用
{name:regex}格式,如/{name:[a-z-]+}。
注意
AntPathMatcher 现已弃用,因为它在处理编码和 URL 复杂性时效率较低。推荐使用 Spring 6.0 默认启用的 PathPatternParser。
模式详细说明
| 模式 | 描述 | 示例 |
|---|---|---|
spring | 字面量模式 | /spring 匹配 /spring |
? | 匹配一个字符 | /t?st 匹配 /test 和 /t3st |
* | 路径段内匹配 | /resources/*.png 匹配 /resources/file.png |
** | 匹配多个路径段 | /resources/** 匹配 /resources/file.png 和 /resources/a/b/c.png |
{name} | 变量捕获 | /owners/{id} 匹配 /owners/1 并捕获 id=1 |
{*path} | 贪婪变量捕获 | /resources/{*file} 匹配 /resources/a/b/c.png 并捕获 file=/a/b/c.png |
媒体类型匹配
- consumes:限制请求的
Content-Type。例如consumes = "application/json"。支持取反表达式如!text/plain。 - produces:限制请求的
Accept标头。例如produces = "application/json"。
参数与标头匹配
你可以根据请求参数或 Header 的存在性、缺失或特定值来缩小映射范围:
params = "myParam":必须存在参数。params = "!myParam":必须不存在参数。params = "myParam=myValue":参数必须等于特定值。headers属性用法相同。
API 版本化 (API Versioning)
在 MVC 配置中启用后,你可以使用 version 属性:
- 固定版本:
version = "1.2"。 - 基线版本:
version = "1.2+"(匹配 1.2 及以上)。
@HttpExchange
虽然原本是为了抽象 HTTP 客户端代码,但 @HttpExchange 也可以在服务器端作为 @RequestMapping 的替代方案。这在内部 API 开发中非常有用,可以增加客户端和服务器之间的耦合(共享接口)。
补充教学
1. 为什么“负向匹配”很有用?
在某些场景下,你可能希望一个方法处理除了某种格式外的使用请求。 例如:consumes = "!application/json"。这常用于兜底处理,确保非 JSON 请求也能获得友好的报错信息。
2. 精通通配符:** vs
**:主要用于路径匹配。它不关心路径的具体内容,只管“过不去”。{*path}:更加现代且强大。它不仅匹配余下的所有路径,还能把这一大串路径全都传给你的变量。这在构建静态资源分发系统(如私有网盘)时非常方便。
3. 理解“最佳匹配”原则
如果一个 URL 被多个模式命中,Spring 怎么选?
- 具体度优先:变量越少、通配符越少的越具体。
- 长度优先:在具体度相同的情况下,模式越长的越优先。
- 解析器优先:
PathPattern比传统的AntPathMatcher更聪明,它在预解析阶段就确定了层级关系。
4. 解决 API 版本化的陷阱
在做多版本并存时,一定要注意 “最接近原则”: 如果请求版本是 1.3,而你定义了 1.2+ 和 1.5。
- Spring 会寻找小于等于 1.3 的最高版本,也就是
1.2+。 - 如果后续你新增了独立的
1.3方法,它会完美接管 1.3 的请求。