Mapping Requests
本部分讨论了带注解控制器的请求映射。
This section discusses request mapping for annotated controllers.
@RequestMapping
@RequestMapping
注释用于将请求映射到控制器方法。它具有根据 URL、HTTP 方法、请求参数、标头和媒体类型进行匹配的各种属性。您可以在类级别使用它来表达共享映射,或者在方法级别使用它来缩小范围到一个特定端点映射。
The @RequestMapping
annotation is used to map requests to controllers methods. It has
various attributes to match by URL, HTTP method, request parameters, headers, and media
types. You can use it at the class level to express shared mappings or at the method level
to narrow down to a specific endpoint mapping.
@RequestMapping
还存在 HTTP 方法特定的快捷方式变体:
There are also HTTP method specific shortcut variants of @RequestMapping
:
-
@GetMapping
-
@PostMapping
-
@PutMapping
-
@DeleteMapping
-
@PatchMapping
前面的注解是 Custom Annotations,之所以提供这些注解,是因为,可以说,大多数控制方法都应该映射到特定的 HTTP 方法,而不是使用 @RequestMapping
,后者默认匹配所有 HTTP 方法。同时,在类级别仍然需要 @RequestMapping
来表示共享映射。
The preceding annotations are Custom Annotations that are provided
because, arguably, most controller methods should be mapped to a specific HTTP method versus
using @RequestMapping
, which, by default, matches to all HTTP methods. At the same time, a
@RequestMapping
is still needed at the class level to express shared mappings.
|
|
以下示例使用类型和方法级映射:
The following example uses type and method level mappings:
-
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 Patterns
可以使用 glob 模式和通配符映射请求:
You can map requests by using glob patterns and wildcards:
Pattern | Description | Example |
---|---|---|
|
Matches one character |
|
|
Matches zero or more characters within a path segment |
|
|
Matches zero or more path segments until the end of the path |
|
|
Matches a path segment and captures it as a variable named "name" |
|
|
Matches the regexp |
|
|
Matches zero or more path segments until the end of the path and captures it as a variable named "path" |
|
可以使用 @PathVariable
访问捕获的 URI 变量,如下例所示:
Captured URI variables can be accessed with @PathVariable
, as the following example shows:
-
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 变量:
You can declare URI variables at the class and method levels, as the following example shows:
- Java
-
@Controller @RequestMapping("/owners/{ownerId}") (1) public class OwnerController { @GetMapping("/pets/{petId}") (2) public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) { // ... } }
1 | Class-level URI mapping. |
2 | Method-level URI mapping.
|
3 | Class-level URI mapping. |
4 | Method-level URI mapping. |
URI 变量自动转换为适当的类型或提出 @26。简单类型(@27、@28、@29 等)默认支持,而且您可以为任何其他数据类型注册支持。请参阅 @31 和 @32。
URI variables are automatically converted to the appropriate type or a TypeMismatchException
is raised. Simple types (int
, long
, Date
, and so on) are supported by default and you can
register support for any other data type.
See Type Conversion and DataBinder
.
可以显式命名 URI 变量(例如,@PathVariable("customId")
),但如果名称相同,并且使用 -parameters
编译器标志编译代码,则可以省去此详细信息。
URI variables can be named explicitly (for example, @PathVariable("customId")
), but you can
leave that detail out if the names are the same and you compile your code with the -parameters
compiler flag.
语法 {*varName}
声明一个匹配零个或多个剩余路径段的 URI 变量。例如,/resources/{*path}
匹配 /resources/
下的所有文件,并且 "path"
变量会捕获 /resources
下的完整路径。
The syntax {*varName}
declares a URI variable that matches zero or more remaining path
segments. For example /resources/{*path}
matches all files under /resources/
, and the
"path"
variable captures the complete path under /resources
.
语法 {varName:regex}
声明一个具有以下语法正则表达式的 URI 变量:{varName:regex}
。例如,给定 URL 为 /spring-web-3.0.5.jar
,则以下方法会提取名称、版本和文件扩展名:
The syntax {varName:regex}
declares a URI variable with a regular expression that has the
syntax: {varName:regex}
. For example, given a URL of /spring-web-3.0.5.jar
, the following method
extracts the name, version, and file extension:
-
Java
-
Kotlin
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
public void handle(@PathVariable String version, @PathVariable String ext) {
// ...
}
@GetMapping("/{name:[a-z-]+}-{version:\\d\\.\\d\\.\\d}{ext:\\.[a-z]+}")
fun handle(@PathVariable version: String, @PathVariable ext: String) {
// ...
}
URI 路径模式还可以包含嵌入式 ${…}
占位符,这些占位符在启动时通过 PropertySourcesPlaceholderConfigurer
针对本地、系统、环境和其他属性源进行解析。例如,可以使用此功能根据某些外部配置对基本 URL 进行参数化。
URI path patterns can also have embedded ${…}
placeholders that are resolved on startup
through PropertySourcesPlaceholderConfigurer
against local, system, environment, and
other property sources. You can use this to, for example, parameterize a base URL based on
some external configuration.
Spring WebFlux 使用 |
Spring WebFlux uses |
Spring WebFlux 不支持后缀模式匹配——与 Spring MVC 不同,后者中像 /person
这样的映射也匹配 /person.*
。如果需要基于 URL 的内容协商,我们建议使用查询参数,它更简单、更明确、也不太容易受到基于 URL 路径的攻击。
Spring WebFlux does not support suffix pattern matching — unlike Spring MVC, where a
mapping such as /person
also matches to /person.*
. For URL-based content
negotiation, if needed, we recommend using a query parameter, which is simpler, more
explicit, and less vulnerable to URL path based exploits.
Pattern Comparison
当多个模式匹配某个 URL 时,必须比较它们以找到最佳匹配。此操作使用 PathPattern.SPECIFICITY_COMPARATOR
完成,后者会寻找更具体的模式。
When multiple patterns match a URL, they must be compared to find the best match. This is done
with PathPattern.SPECIFICITY_COMPARATOR
, which looks for patterns that are more specific.
对于每个模式,都会基于 URI 变量和通配符的数量计算一个分数,其中 URI 变量的分数低于通配符。总分较低的模式获胜。如果两个模式的分数相同,则选择较长的模式。
For every pattern, a score is computed, based on the number of URI variables and wildcards, where a URI variable scores lower than a wildcard. A pattern with a lower total score wins. If two patterns have the same score, the longer is chosen.
总括模式(例如 **
、{*varName}
)不包含在评分中,并且始终被排序到最后。如果两个模式都是总括模式,则选择较长的模式。
Catch-all patterns (for example, **
, {*varName}
) are excluded from the scoring and are always
sorted last instead. If two patterns are both catch-all, the longer is chosen.
Consumable Media Types
您可以根据请求的 Content-Type
缩小请求映射的范围,如下例所示:
You can narrow the request mapping based on the Content-Type
of the request,
as the following example shows:
-
Java
-
Kotlin
@PostMapping(path = "/pets", consumes = "application/json")
public void addPet(@RequestBody Pet pet) {
// ...
}
@PostMapping("/pets", consumes = ["application/json"])
fun addPet(@RequestBody pet: Pet) {
// ...
}
consumes
属性还支持否定表达式——例如,!text/plain
表示任何不是 text/plain
的内容类型。
The consumes attribute also supports negation expressions — for example, !text/plain
means any
content type other than text/plain
.
可以在类级别声明共享的 consumes
属性。然而,与大多数其他请求映射属性不同,在类级别使用时,方法级别的 consumes
属性会替代类级别的声明,而不是进行扩展。
You can declare a shared consumes
attribute at the class level. Unlike most other request
mapping attributes, however, when used at the class level, a method-level consumes
attribute
overrides rather than extends the class-level declaration.
|
|
Producible Media Types
你可以根据 Accept
请求头和控制器方法生成的长度类型列表来缩小请求映射范围,如下例所示:
You can narrow the request mapping based on the Accept
request header and the list of
content types that a controller method produces, as the following example shows:
-
Java
-
Kotlin
@GetMapping(path = "/pets/{petId}", produces = "application/json")
@ResponseBody
public Pet getPet(@PathVariable String petId) {
// ...
}
@GetMapping("/pets/{petId}", produces = ["application/json"])
@ResponseBody
fun getPet(@PathVariable petId: String): Pet {
// ...
}
媒体类型可以指定一个字符集。支持否定表达 — 例如,!text/plain
表示任何内容类型,而不是 text/plain
。
The media type can specify a character set. Negated expressions are supported — for example,
!text/plain
means any content type other than text/plain
.
您可以在类级别声明共享的 produces
属性。但是,与大多数其他请求映射属性不同,当在类级别使用时,方法级别的 produces
属性会覆盖而不是扩展类级别声明。
You can declare a shared produces
attribute at the class level. Unlike most other request
mapping attributes, however, when used at the class level, a method-level produces
attribute
overrides rather than extend the class level declaration.
|
|
Parameters and Headers
您可以基于查询参数条件缩小请求映射。您可以测试查询参数是否存在 (myParam
),缺少 (!myParam
),或存在特定值 (myParam=myValue
)。以下示例测试带值的某个参数:
You can narrow request mappings based on query parameter conditions. You can test for the
presence of a query parameter (myParam
), for its absence (!myParam
), or for a
specific value (myParam=myValue
). The following examples tests for a parameter with a value:
- Java
-
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") (1) public void findPet(@PathVariable String petId) { // ... }
1 | Check that myParam equals myValue .
|
2 | Check that myParam equals myValue . |
你还可以使用与请求头的条件相同的条件,如下例所示:
You can also use the same with request header conditions, as the following example shows:
- Java
-
@GetMapping(path = "/pets/{petId}", headers = "myHeader=myValue") (1) public void findPet(@PathVariable String petId) { // ... }
1 | Check that myHeader equals myValue .
|
2 | Check that myHeader equals myValue . |
HTTP HEAD, OPTIONS
@GetMapping
和 @RequestMapping(method=HttpMethod.GET)
会透明地支持 HTTP HEAD 以用于请求映射。控制器方法无需更改。应用到 HttpHandler
服务器适配器中的响应包装器会确保将 Content-Length
头部设置为已写入的字节数,而无需实际写入响应。
@GetMapping
and @RequestMapping(method=HttpMethod.GET)
support HTTP HEAD
transparently for request mapping purposes. Controller methods need not change.
A response wrapper, applied in the HttpHandler
server adapter, ensures a Content-Length
header is set to the number of bytes written without actually writing to the response.
默认情况下,HTTP OPTIONS 会通过将 Allow
响应头设置为所有具有匹配 URL 模式的 @RequestMapping
方法中列出的 HTTP 方法列表来进行处理。
By default, HTTP OPTIONS is handled by setting the Allow
response header to the list of HTTP
methods listed in all @RequestMapping
methods with matching URL patterns.
对于没有 HTTP 方法声明的 @RequestMapping
,Allow
头部会设置为 GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
。控制器方法应始终声明所支持的 HTTP 方法(例如,通过使用 HTTP 方法特定的变体 @GetMapping
、@PostMapping
等)。
For a @RequestMapping
without HTTP method declarations, the Allow
header is set to
GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS
. Controller methods should always declare the
supported HTTP methods (for example, by using the HTTP method specific variants — @GetMapping
, @PostMapping
, and others).
您可以将 @RequestMapping
方法显式映射到 HTTP HEAD 和 HTTP OPTIONS,但在通常情况下无需这样做。
You can explicitly map a @RequestMapping
method to HTTP HEAD and HTTP OPTIONS, but that
is not necessary in the common case.
Custom Annotations
Spring WebFlux 支持使用 composed annotations 进行请求映射。这些是使用 @RequestMapping
进行元注解且合成以重新声明 @RequestMapping
属性的一个子集(或全部)的注解,其具有较窄、更具体的目的。
Spring WebFlux supports the use of composed annotations
for request mapping. Those are annotations that are themselves meta-annotated with
@RequestMapping
and composed to redeclare a subset (or all) of the @RequestMapping
attributes with a narrower, more specific purpose.
@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
和 @PatchMapping
是组合注释的示例。提供这些注释是因为从理论上讲,大多数控制器方法都应该映射到特定的 HTTP 方法,而不是使用默认情况下与所有 HTTP 方法匹配的 @RequestMapping
。如果您需要有关如何实现组合注释的示例,请查看如何声明这些注释。
@GetMapping
, @PostMapping
, @PutMapping
, @DeleteMapping
, and @PatchMapping
are
examples of composed annotations. They are provided, because, arguably, most
controller methods should be mapped to a specific HTTP method versus using @RequestMapping
,
which, by default, matches to all HTTP methods. If you need an example of how to implement
a composed annotation, look at how those are declared.
|
|
Spring WebFlux 还支持具有自定义请求匹配逻辑的自定义请求映射属性。这是一个更高级的选项,需要子类化 RequestMappingHandlerMapping
并覆盖 getCustomMethodCondition
方法,您可以在其中检查自定义属性并返回您自己的 RequestCondition
。
Spring WebFlux also supports custom request mapping attributes with custom request matching
logic. This is a more advanced option that requires sub-classing
RequestMappingHandlerMapping
and overriding the getCustomMethodCondition
method, where
you can check the custom attribute and return your own RequestCondition
.
Explicit Registrations
您可以以编程方式注册处理程序方法,可用于动态注册或高级案例,例如不同 URL 下相同处理程序的不同实例。以下示例展示了如何执行此操作:
You can programmatically register Handler methods, which can be used for dynamic registrations or for advanced cases, such as different instances of the same handler under different URLs. The following example shows how to do so:
- Java
-
@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 | Inject target handlers and the handler mapping for controllers. |
2 | Prepare the request mapping metadata. |
3 | Get the handler method. |
4 | Add the registration.
|
5 | Inject target handlers and the handler mapping for controllers. |
6 | Prepare the request mapping metadata. |
7 | Get the handler method. |
8 | Add the registration. |
@HttpExchange
虽然 @HttpExchange
的主要目的是使用生成的代理抽象 HTTP 客户端代码,但是放置此类注释的 HTTP Interface 是一个对于客户端和服务器使用而言呈中立态度的合约。除了简化客户端代码之外,还有部分情况下,一个 HTTP 接口可能是一种便利的方法,让服务器为客户端访问公开其 API。这会导致客户端与服务器耦合度升高,对于公共 API,这往往不是一个好选择,但对于内部 API 来说,它可能正是目标。这在 Spring Cloud 中是一种常用的方法,这也是 @HttpExchange
作为服务器端处理的替代方案在控制器类中受支持的原因。
While the main purpose of @HttpExchange
is to abstract HTTP client code with a
generated proxy, the
HTTP Interface on which
such annotations are placed is a contract neutral to client vs server use.
In addition to simplifying client code, there are also cases where an HTTP Interface
may be a convenient way for servers to expose their API for client access. This leads
to increased coupling between client and server and is often not a good choice,
especially for public API’s, but may be exactly the goal for an internal API.
It is an approach commonly used in Spring Cloud, and it is why @HttpExchange
is
supported as an alternative to @RequestMapping
for server side handling in
controller classes.
例如:
For example:
-
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
and @RequestMapping
have differences.
@RequestMapping
can map to any number of requests by path patterns, HTTP methods,
and more, while @HttpExchange
declares a single endpoint with a concrete HTTP method,
path, and content types.
对于方法参数和返回值,通常,@HttpExchange
支持 @RequestMapping
支持的方法参数的一个子集。值得注意的是,它排除了任何服务器端特定参数类型。有关详细信息,请参见 @HttpExchange 和 @RequestMapping 的列表。
For method parameters and returns values, generally, @HttpExchange
supports a
subset of the method parameters that @RequestMapping
does. Notably, it excludes any
server-side specific parameter types. For details, see the list for
@HttpExchange and
@RequestMapping.