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)
1 使用 URI 模板的静态工厂方法。
2 添加或替换 URI 组件。
3 请求对 URI 模板和 URI 变量进行编码。
4 构建一个 UriComponents
5 扩展变量并获取 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)
1 使用 URI 模板的静态工厂方法。
2 添加或替换 URI 组件。
3 请求对 URI 模板和 URI 变量进行编码。
4 构建一个 UriComponents
5 扩展变量并获取 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 创建一个 UriBuilderUriBuilderFactoryUriBuilder 共同提供了一种可插拔机制,用于根据共享配置(例如基本 URL、编码首选项和其他详细信息)从 URI 模板构建 URI。

您可以使用 UriBuilderFactory 配置 RestTemplateWebClient 以自定义 URI 的准备工作。DefaultUriBuilderFactoryUriBuilderFactory 的默认实现,它在内部使用 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 解析器类型

  1. RFC 解析器——此解析器类型期望 URI 字符串符合 RFC 3986 语法,并将与语法偏差视为非法。

  2. WhatWG 解析器——此解析器基于 WhatWG URL 活跃标准 中的 URL 解析算法。它提供了对各种意外输入情况的宽容处理。浏览器实现此功能是为了宽容地处理用户输入的 URL。有关更多详细信息,请参阅 URL 活跃标准和 URL 解析 测试用例

默认情况下,RestClientWebClientRestTemplate 使用 RFC 解析器类型,并期望应用程序提供符合 RFC 语法的 URL 模板。要更改它,您可以在任何客户端上自定义 UriBuilderFactory

应用程序和框架可以进一步依赖 UriComponentsBuilder 来满足其自身的需求,以解析用户提供的 URL,以便检查并可能验证 URI 组件(例如方案、主机、端口、路径和查询)。这些组件可以决定使用 WhatWG 解析器类型来更宽容地处理 URL,并与浏览器解析 URI 的方式保持一致,以防重定向到输入 URL 或将其包含在对浏览器的响应中。

URI 编码

Spring MVC 和 Spring WebFlux

UriComponentsBuilder 在两个级别公开编码选项

这两个选项都将非 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")

WebClientRestTemplate 通过 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_COMPONENTWebClient 依赖于 DefaultUriBuilderFactory 中的默认值,该值在 5.0.x 中从 EncodingMode.URI_COMPONENT 更改为 5.1 中的 EncodingMode.TEMPLATE_AND_VALUES