URI 链接
本节介绍 Spring 框架中可用于准备 URI 的各种选项。
UriComponents
Spring MVC 和 Spring WebFlux
UriComponentsBuilder
帮助从带有变量的 URI 模板构建 URI,如以下示例所示:
- Java
- Kotlin
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
使用 URI 模板的静态工厂方法。
添加或替换 URI 组件。
请求对 URI 模板和 URI 变量进行编码。
构建一个
UriComponents
。展开变量并获取
URI
。
val uriComponents = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}") 1
.queryParam("q", "{q}") 2
.encode() 3
.build() 4
val uri = uriComponents.expand("Westin", "123").toUri() 5
使用 URI 模板的静态工厂方法。
添加或替换 URI 组件。
请求对 URI 模板和 URI 变量进行编码。
构建一个
UriComponents
。展开变量并获取
URI
。
前面的示例可以通过 buildAndExpand
合并为一个链并缩短,如以下示例所示:
- 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(这意味着编码)来进一步缩短它,如以下示例所示:
- 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 模板进一步缩短它,如以下示例所示:
- 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
Spring MVC 和 Spring WebFlux
UriComponentsBuilder 实现了 UriBuilder
。你可以通过 UriBuilderFactory
创建一个 UriBuilder
。UriBuilderFactory
和 UriBuilder
一起提供了一种可插拔的机制,用于根据共享配置(如基础 URL、编码偏好和其他细节)从 URI 模板构建 URI。
您可以使用 UriBuilderFactory
配置 RestTemplate
和 WebClient
以自定义 URI 的准备。DefaultUriBuilderFactory
是 UriBuilderFactory
的默认实现,它在内部使用 UriComponentsBuilder
并公开共享的配置选项。
下面的示例展示了如何配置一个 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
:
- 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
类似,但它不是静态工厂方法,而是一个实际的实例,持有配置和偏好,如以下示例所示:
- 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 解析
Spring MVC 和 Spring WebFlux
UriComponentsBuilder
支持两种 URI 解析器类型:
-
RFC 解析器 — 这种解析器类型要求 URI 字符串符合 RFC 3986 语法,并将语法的偏差视为非法。
-
WhatWG 解析器 — 这种解析器基于 URL 解析算法 在 WhatWG URL Living standard 中。它提供了对各种意外输入情况的宽松处理。浏览器实现这一点是为了宽松地处理用户输入的 URL。有关更多详细信息,请参阅 URL Living Standard 和 URL 解析测试用例。
默认情况下,RestClient
、WebClient
和 RestTemplate
使用 RFC 解析器类型,并期望应用程序提供符合 RFC 语法的 URL 模板。要更改这一点,您可以在任何客户端上自定义 UriBuilderFactory
。
应用程序和框架可能会进一步依赖 UriComponentsBuilder
来解析用户提供的 URL,以便检查和可能验证 URI 组件,例如方案、主机、端口、路径和查询。这些组件可以选择使用 WhatWG 解析器类型,以更宽松地处理 URL,并与浏览器解析 URI 的方式保持一致,以防重定向到输入 URL 或者它被包含在对浏览器的响应中。
URI 编码
Spring MVC 和 Spring WebFlux
UriComponentsBuilder
在两个级别上提供编码选项:
-
UriComponentsBuilder#encode(): 先对 URI 模板进行预编码,然后在扩展时严格编码 URI 变量。
-
UriComponents#encode(): 在 URI 变量扩展之后对 URI 组件进行编码。
这两个选项都将非 ASCII 和非法字符替换为转义八位字节。然而,第一个选项还会替换出现在 URI 变量中的具有保留意义的字符。
考虑“;”,它在路径中是合法的,但具有保留意义。第一个选项在 URI 变量中将“;”替换为“%3B”,但在 URI 模板中不替换。相比之下,第二个选项从不替换“;”,因为它在路径中是一个合法字符。
在大多数情况下,第一个选项可能会给出预期的结果,因为它将 URI 变量视为需要完全编码的不透明数据,而第二个选项在 URI 变量有意包含保留字符时很有用。第二个选项在根本不展开 URI 变量时也很有用,因为这也会对任何看起来像 URI 变量的内容进行编码。
以下示例使用了第一种选项:
- 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(这意味着编码)来缩短前面的示例,如以下示例所示:
- 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 模板进一步缩短它,如以下示例所示:
- 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 模板。两者都可以配置自定义策略,如以下示例所示:
- 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 模板。作为一个工厂,它提供了一个配置编码方法的单一位置,基于以下编码模式之一:
-
TEMPLATE_AND_VALUES
:使用UriComponentsBuilder#encode()
,对应于前面列表中的第一个选项,预先编码 URI 模板,并在展开时严格编码 URI 变量。 -
VALUES_ONLY
:不编码 URI 模板,而是通过UriUtils#encodeUriVariables
对 URI 变量进行严格编码,然后再将其展开到模板中。 -
URI_COMPONENT
:使用UriComponents#encode()
,对应于前面列表中的第二个选项,在 URI 变量展开后对 URI 组件值进行编码。 -
NONE
:不进行编码。
RestTemplate
被设置为 EncodingMode.URI_COMPONENT
是出于历史原因和向后兼容性考虑。WebClient
依赖于 DefaultUriBuilderFactory
中的默认值,该默认值在 5.0.x 中从 EncodingMode.URI_COMPONENT
更改为 5.1 中的 EncodingMode.TEMPLATE_AND_VALUES
。