URI Links
此部分介绍了 Spring Framework 中用于处理 URI 的各种可用选项。
This section describes various options available in the Spring Framework to work with URI’s.
UriComponents
-
通过静态工厂方法从 URI 模板创建 URI 组件。
-
添加或替换 URI 组件,例如查询参数或路径变量。
-
对 URI 模板和 URI 变量进行编码,以确保跨网络安全传输。
-
构建 UriComponents 对象,其中包含 URI 模板和变量。
-
展开变量并获取 URI,生成最终的请求 URI。 Spring MVC and Spring WebFlux
UriComponentsBuilder
有助于通过具有变量的 URI 模板来构建 URI,如下例所示:
UriComponentsBuilder
helps to build URI’s from URI templates with variables, as the following example shows:
- Java
-
UriComponents uriComponents = UriComponentsBuilder .fromUriString("https://example.com/hotels/{hotel}") (1) .queryParam("q", "{q}") (2) .encode() (3) .build(); (4) URI uri = uriComponents.expand("Westin", "123").toUri(); (5)
1 | Static factory method with a URI template. |
2 | Add or replace URI components. |
3 | Request to have the URI template and URI variables encoded. |
4 | Build a UriComponents . |
5 | Expand variables and obtain the URI .
|
6 | Static factory method with a URI template. |
7 | Add or replace URI components. |
8 | Request to have the URI template and URI variables encoded. |
9 | Build a UriComponents . |
10 | Expand variables and obtain the URI . |
前面的示例可以合并到一个链中并使用 buildAndExpand
缩短,如下例所示:
The preceding example can be consolidated into one chain and shortened with buildAndExpand
,
as the following example shows:
-
Java
-
Kotlin
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("Westin", "123")
.toUri();
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("Westin", "123")
.toUri()
您可以更进一步缩短它,直接转到 URI(这意味着编码),如下例所示:
You can shorten it further by going directly to a URI (which implies encoding), as the following example shows:
-
Java
-
Kotlin
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123");
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123")
您还可以使用完整的 URI 模板进一步缩短它,如下例所示:
You can shorten it further still with a full URI template, as the following example shows:
-
Java
-
Kotlin
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
.build("Westin", "123");
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
.build("Westin", "123")
UriBuilder
[role="small"][.small]Spring MVC 和 Spring WebFlux
[role="small"][.small]Spring MVC and Spring WebFlux
<<`UriComponentsBuilder`,web-uricomponents>> 实现 UriBuilder
。您可以创建一个 UriBuilder
,反过来,可以使用 UriBuilderFactory
创建一个 UriBuilder
。UriBuilderFactory
和 UriBuilder
一起提供了一种可插入机制,用于基于共享配置(例如基本 URL、编码首选项和其他详细信息)从 URI 模板构建 URI。
<<`UriComponentsBuilder`,web-uricomponents>> implements UriBuilder
. You can create a
UriBuilder
, in turn, with a UriBuilderFactory
. Together, UriBuilderFactory
and
UriBuilder
provide a pluggable mechanism to build URIs from URI templates, based on
shared configuration, such as a base URL, encoding preferences, and other details.
您可以使用 UriBuilderFactory
配置 RestTemplate
和 WebClient
以自定义 URI 的准备。DefaultUriBuilderFactory
是 UriBuilderFactory
的默认实现,它在内部使用 UriComponentsBuilder
并公开共享配置选项。
You can configure RestTemplate
and WebClient
with a UriBuilderFactory
to customize the preparation of URIs. DefaultUriBuilderFactory
is a default
implementation of UriBuilderFactory
that uses UriComponentsBuilder
internally and
exposes shared configuration options.
以下示例显示如何配置 RestTemplate
:
The following example shows how to configure a RestTemplate
:
-
Java
-
Kotlin
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
String baseUrl = "https://example.org";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode
val baseUrl = "https://example.org"
val factory = DefaultUriBuilderFactory(baseUrl)
factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES
val restTemplate = RestTemplate()
restTemplate.uriTemplateHandler = factory
以下示例配置 WebClient
:
The following example configures a WebClient
:
-
Java
-
Kotlin
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode;
String baseUrl = "https://example.org";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl);
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
// import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode
val baseUrl = "https://example.org"
val factory = DefaultUriBuilderFactory(baseUrl)
factory.encodingMode = EncodingMode.TEMPLATE_AND_VALUES
val client = WebClient.builder().uriBuilderFactory(factory).build()
此外,您还可以直接使用 DefaultUriBuilderFactory
。它类似于使用 UriComponentsBuilder
,但不是静态工厂方法,而是一个实际的实例,包含配置和首选项,如下例所示:
In addition, you can also use DefaultUriBuilderFactory
directly. It is similar to using
UriComponentsBuilder
but, instead of static factory methods, it is an actual instance
that holds configuration and preferences, as the following example shows:
-
Java
-
Kotlin
String baseUrl = "https://example.com";
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
URI uri = uriBuilderFactory.uriString("/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123");
val baseUrl = "https://example.com"
val uriBuilderFactory = DefaultUriBuilderFactory(baseUrl)
val uri = uriBuilderFactory.uriString("/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123")
URI Encoding
[role="small"][.small]Spring MVC 和 Spring WebFlux
[role="small"][.small]Spring MVC and Spring WebFlux
UriComponentsBuilder
在两个级别上公开编码选项:
UriComponentsBuilder
exposes encoding options at two levels:
-
UriComponentsBuilder#encode(): Pre-encodes the URI template first and then strictly encodes URI variables when expanded.
-
UriComponents#encode(): Encodes URI components after URI variables are expanded.
这两个选项都将非 ASCII 和非法字符替换为转义的八位字节序列。但是,第一个选项还将出现在 URI 变量中的保留含义字符替换为转义字符。
Both options replace non-ASCII and illegal characters with escaped octets. However, the first option also replaces characters with reserved meaning that appear in URI variables.
考虑“;”这个分号在路径中是合法的,但在 URI 模板中不合法,保留着含义。第一个选项用 URI 变量中的“;”替换为“%3B”,但不会将 URI 模板中的“;”替换为“%3B”。相比之下,第二个选项永远不会替换“;”,因为它是路径中的一个合法字符。 |
Consider ";", which is legal in a path but has reserved meaning. The first option replaces ";" with "%3B" in URI variables but not in the URI template. By contrast, the second option never replaces ";", since it is a legal character in a path. |
对于大多数情况,第一个选项可能会提供预期的结果,因为它将 URI 变量作为要完全编码的不透明数据进行处理,而第二个选项在 URI 变量有意包含保留字符时很有用。第二个选项在根本未扩展 URI 变量时也很有用,因为这也会对偶发 trông giống như một biến URI 的任何内容进行编码。
For most cases, the first option is likely to give the expected result, because it treats URI variables as opaque data to be fully encoded, while the second option is useful if URI variables do intentionally contain reserved characters. The second option is also useful when not expanding URI variables at all since that will also encode anything that incidentally looks like a URI variable.
以下示例使用第一个选项:
The following example uses the first option:
-
Java
-
Kotlin
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("New York", "foo+bar")
.toUri();
// Result is "/hotel%20list/New%20York?q=foo%2Bbar"
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("New York", "foo+bar")
.toUri()
// Result is "/hotel%20list/New%20York?q=foo%2Bbar"
您可以通过直接转到 URI(这意味着编码)来缩短前面的示例,如下例所示:
You can shorten the preceding example by going directly to the URI (which implies encoding), as the following example shows:
-
Java
-
Kotlin
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.build("New York", "foo+bar");
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.build("New York", "foo+bar")
您还可以使用完整的 URI 模板进一步缩短它,如下例所示:
You can shorten it further still with a full URI template, as the following example shows:
-
Java
-
Kotlin
URI uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
.build("New York", "foo+bar");
val uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
.build("New York", "foo+bar")
WebClient
和 RestTemplate
通过 UriBuilderFactory
策略在内部展开和编码 URI 模板。两个都可以使用自定义策略进行配置,如下例所示:
The WebClient
and the RestTemplate
expand and encode URI templates internally through
the UriBuilderFactory
strategy. Both can be configured with a custom strategy,
as the following example shows:
-
Java
-
Kotlin
String baseUrl = "https://example.com";
DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(baseUrl)
factory.setEncodingMode(EncodingMode.TEMPLATE_AND_VALUES);
// Customize the RestTemplate..
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);
// Customize the WebClient..
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
val baseUrl = "https://example.com"
val factory = DefaultUriBuilderFactory(baseUrl).apply {
encodingMode = EncodingMode.TEMPLATE_AND_VALUES
}
// Customize the RestTemplate..
val restTemplate = RestTemplate().apply {
uriTemplateHandler = factory
}
// Customize the WebClient..
val client = WebClient.builder().uriBuilderFactory(factory).build()
DefaultUriBuilderFactory
实现内部使用 UriComponentsBuilder
来展开和编码 URI 模板。作为工厂,它提供了一个单一的位置来配置编码方法,基于以下编码模式之一:
The DefaultUriBuilderFactory
implementation uses UriComponentsBuilder
internally to
expand and encode URI templates. As a factory, it provides a single place to configure
the approach to encoding, based on one of the below encoding modes:
-
TEMPLATE_AND_VALUES
: UsesUriComponentsBuilder#encode()
, corresponding to the first option in the earlier list, to pre-encode the URI template and strictly encode URI variables when expanded. -
VALUES_ONLY
: Does not encode the URI template and, instead, applies strict encoding to URI variables throughUriUtils#encodeUriVariables
prior to expanding them into the template. -
URI_COMPONENT
: UsesUriComponents#encode()
, corresponding to the second option in the earlier list, to encode URI component value after URI variables are expanded. -
NONE
: No encoding is applied.
将 RestTemplate
设置为 EncodingMode.URI_COMPONENT
是出于历史原因和向后兼容性的考虑。WebClient
依赖于 DefaultUriBuilderFactory
中的默认值,该值已从 5.0.x 中 的 EncodingMode.URI_COMPONENT
更改为 5.1 中的 EncodingMode.TEMPLATE_AND_VALUES
。
The RestTemplate
is set to EncodingMode.URI_COMPONENT
for historic
reasons and for backwards compatibility. The WebClient
relies on the default value
in DefaultUriBuilderFactory
, which was changed from EncodingMode.URI_COMPONENT
in
5.0.x to EncodingMode.TEMPLATE_AND_VALUES
in 5.1.
Relative Servlet Requests
你可以使用 ServletUriComponentsBuilder
创建相对于当前请求的 URI,如下例所示:
You can use ServletUriComponentsBuilder
to create URIs relative to the current request,
as the following example shows:
-
Java
-
Kotlin
HttpServletRequest request = ...
// Re-uses scheme, host, port, path, and query string...
URI uri = ServletUriComponentsBuilder.fromRequest(request)
.replaceQueryParam("accountId", "{id}")
.build("123");
val request: HttpServletRequest = ...
// Re-uses scheme, host, port, path, and query string...
val uri = ServletUriComponentsBuilder.fromRequest(request)
.replaceQueryParam("accountId", "{id}")
.build("123")
你可以创建相对于上下文路径的 URI,如下例所示:
You can create URIs relative to the context path, as the following example shows:
-
Java
-
Kotlin
HttpServletRequest request = ...
// Re-uses scheme, host, port, and context path...
URI uri = ServletUriComponentsBuilder.fromContextPath(request)
.path("/accounts")
.build()
.toUri();
val request: HttpServletRequest = ...
// Re-uses scheme, host, port, and context path...
val uri = ServletUriComponentsBuilder.fromContextPath(request)
.path("/accounts")
.build()
.toUri()
你可以创建相对于某个 Servlet(例如,/main/*
)的 URI,如下例所示:
You can create URIs relative to a Servlet (for example, /main/*
),
as the following example shows:
-
Java
-
Kotlin
HttpServletRequest request = ...
// Re-uses scheme, host, port, context path, and Servlet mapping prefix...
URI uri = ServletUriComponentsBuilder.fromServletMapping(request)
.path("/accounts")
.build()
.toUri();
val request: HttpServletRequest = ...
// Re-uses scheme, host, port, context path, and Servlet mapping prefix...
val uri = ServletUriComponentsBuilder.fromServletMapping(request)
.path("/accounts")
.build()
.toUri()
从 5.1 开始, |
As of 5.1, |
Links to Controllers
Spring MVC 提供一种准备连接器到控制器方法的机制。例如,下面的 MVC 控制允许连接创建:
Spring MVC provides a mechanism to prepare links to controller methods. For example, the following MVC controller allows for link creation:
-
Java
-
Kotlin
@Controller
@RequestMapping("/hotels/{hotel}")
public class BookingController {
@GetMapping("/bookings/{booking}")
public ModelAndView getBooking(@PathVariable Long booking) {
// ...
}
}
@Controller
@RequestMapping("/hotels/{hotel}")
class BookingController {
@GetMapping("/bookings/{booking}")
fun getBooking(@PathVariable booking: Long): ModelAndView {
// ...
}
}
您可以通过按名称引用方法来准备链接,如下面的示例所示:
You can prepare a link by referring to the method by name, as the following example shows:
-
Java
-
Kotlin
UriComponents uriComponents = MvcUriComponentsBuilder
.fromMethodName(BookingController.class, "getBooking", 21).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
val uriComponents = MvcUriComponentsBuilder
.fromMethodName(BookingController::class.java, "getBooking", 21).buildAndExpand(42)
val uri = uriComponents.encode().toUri()
在前面的示例中,我们提供了用作路径变量并插入 URL 中的实际方法参数值(在本例中是长整型值:21
)。此外,我们提供数值 42
来补充任何剩余的 URI 变量,例如从类型级别请求映射继承的 hotel
变量。如果方法有更多参数,则我们可以为不需要的 URL 参数提供 null 值。通常,只有 @PathVariable
和 @RequestParam
参数与构造 URL 有关。
In the preceding example, we provide actual method argument values (in this case, the long value: 21
)
to be used as a path variable and inserted into the URL. Furthermore, we provide the
value, 42
, to fill in any remaining URI variables, such as the hotel
variable inherited
from the type-level request mapping. If the method had more arguments, we could supply null for
arguments not needed for the URL. In general, only @PathVariable
and @RequestParam
arguments
are relevant for constructing the URL.
还有其他使用 MvcUriComponentsBuilder
的方法。例如,您可以使用类似于通过代理进行模拟测试的技术,以避免按名称引用控制器方法,如下面的示例所示(该示例假设静态导入了 MvcUriComponentsBuilder.on
):
There are additional ways to use MvcUriComponentsBuilder
. For example, you can use a technique
akin to mock testing through proxies to avoid referring to the controller method by name, as the following example shows
(the example assumes static import of MvcUriComponentsBuilder.on
):
-
Java
-
Kotlin
UriComponents uriComponents = MvcUriComponentsBuilder
.fromMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
val uriComponents = MvcUriComponentsBuilder
.fromMethodCall(on(BookingController::class.java).getBooking(21)).buildAndExpand(42)
val uri = uriComponents.encode().toUri()
当控制器方法签名用于通过 |
Controller method signatures are limited in their design when they are supposed to be usable for
link creation with |
前面的示例使用 MvcUriComponentsBuilder
中的静态方法。在内部,它们依赖于 ServletUriComponentsBuilder
来根据当前请求的协议、主机、端口、上下文路径和 servlet 路径准备一个基础 URL。这在大多数情况下都能正常工作。但是,有时可能不够用。例如,您可能不在请求的上下文中(例如准备链接的批处理进程),或者您可能需要插入路径前缀(例如从请求路径中移除的区域设置前缀,需要重新插入链接中)。
The earlier examples use static methods in MvcUriComponentsBuilder
. Internally, they rely
on ServletUriComponentsBuilder
to prepare a base URL from the scheme, host, port,
context path, and servlet path of the current request. This works well in most cases.
However, sometimes, it can be insufficient. For example, you may be outside the context of
a request (such as a batch process that prepares links) or perhaps you need to insert a path
prefix (such as a locale prefix that was removed from the request path and needs to be
re-inserted into links).
对于此类情况,您可以使用接受 UriComponentsBuilder
的静态 fromXxx
重载方法来使用基础 URL。或者,您可以使用基础 URL 创建 MvcUriComponentsBuilder
的实例,然后使用基于实例的 withXxx
方法。例如,下面的清单使用了 withMethodCall
:
For such cases, you can use the static fromXxx
overloaded methods that accept a
UriComponentsBuilder
to use a base URL. Alternatively, you can create an instance of MvcUriComponentsBuilder
with a base URL and then use the instance-based withXxx
methods. For example, the
following listing uses withMethodCall
:
-
Java
-
Kotlin
UriComponentsBuilder base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en");
MvcUriComponentsBuilder builder = MvcUriComponentsBuilder.relativeTo(base);
builder.withMethodCall(on(BookingController.class).getBooking(21)).buildAndExpand(42);
URI uri = uriComponents.encode().toUri();
val base = ServletUriComponentsBuilder.fromCurrentContextPath().path("/en")
val builder = MvcUriComponentsBuilder.relativeTo(base)
builder.withMethodCall(on(BookingController::class.java).getBooking(21)).buildAndExpand(42)
val uri = uriComponents.encode().toUri()
从 5.1 开始, |
As of 5.1, |
Links in Views
在 Thymeleaf、FreeMarker 或 JSP 等视图中,您可以通过引用每个请求映射的隐式或显式分配名称来构建指向带注释控制器的链接。
In views such as Thymeleaf, FreeMarker, or JSP, you can build links to annotated controllers by referring to the implicitly or explicitly assigned name for each request mapping.
请考虑以下示例:
Consider the following example:
-
Java
-
Kotlin
@RequestMapping("/people/{id}/addresses")
public class PersonAddressController {
@RequestMapping("/{country}")
public HttpEntity<PersonAddress> getAddress(@PathVariable String country) { ... }
}
@RequestMapping("/people/{id}/addresses")
class PersonAddressController {
@RequestMapping("/{country}")
fun getAddress(@PathVariable country: String): HttpEntity<PersonAddress> { ... }
}
给定了前面的控制器,您可以按以下方式从 JSP 准备链接:
Given the preceding controller, you can prepare a link from a JSP, as follows:
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
...
<a href="${s:mvcUrl('PAC#getAddress').arg(0,'US').buildAndExpand('123')}">Get Address</a>
前面的示例依赖于在 Spring 标签库(即 META-INF/spring.tld)中声明的 mvcUrl
函数,但是,定义您自己的函数或为其他模板技术准备类似的函数很容易。
The preceding example relies on the mvcUrl
function declared in the Spring tag library
(that is, META-INF/spring.tld), but it is easy to define your own function or prepare a
similar one for other templating technologies.
它就是这样工作的。在启动时,每个 @RequestMapping
都通过 HandlerMethodMappingNamingStrategy
分配一个默认名称,其默认实现使用类和方法名称首字母(例如,ThingController
中的 getThing
方法变为“TC#getThing”)。如果有名称冲突,您可以使用 @RequestMapping(name="..")
分配显式名称或实现您自己的 HandlerMethodMappingNamingStrategy
。
Here is how this works. On startup, every @RequestMapping
is assigned a default name
through HandlerMethodMappingNamingStrategy
, whose default implementation uses the
capital letters of the class and the method name (for example, the getThing
method in
ThingController
becomes "TC#getThing"). If there is a name clash, you can use
@RequestMapping(name="..")
to assign an explicit name or implement your own
HandlerMethodMappingNamingStrategy
.