对于最新的稳定版本,请使用 Spring Framework 7.0.6!spring-doc.cadn.net.cn

URI链接

本节描述了Spring框架中可用的各种准备URI的选项。spring-doc.cadn.net.cn

URI组件

Spring MVC 和 Spring WebFluxspring-doc.cadn.net.cn

UriComponentsBuilder 帮助根据带有变量的URI模板构建URI,如下例所示:spring-doc.cadn.net.cn

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 缩短, 如下例所示:spring-doc.cadn.net.cn

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(这隐含了编码),如下例所示:spring-doc.cadn.net.cn

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模板来缩短它,如下例所示:spring-doc.cadn.net.cn

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")

URI构建器

Spring MVC 和 Spring WebFluxspring-doc.cadn.net.cn

UriComponentsBuilder 实现 UriBuilder. 您可以创建一个 UriBuilder,进而使用 UriBuilderFactory. 一起,UriBuilderFactoryUriBuilder 提供了一个可插拔的机制来根据 URI 模板构建 URI,基于共享配置,例如基础 URL、编码偏好和其他细节。spring-doc.cadn.net.cn

您可以使用 RestTemplateWebClient 配置 UriBuilderFactory 来自定义 URI 的准备。DefaultUriBuilderFactoryUriBuilderFactory 的默认实现,内部使用 UriComponentsBuilder 并公开共享的配置选项。spring-doc.cadn.net.cn

以下示例展示了如何配置一个RestTemplatespring-doc.cadn.net.cn

// 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

以下示例配置了一个WebClientspring-doc.cadn.net.cn

// 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,但不同的是,它是一个实际的实例,用于保存配置和偏好设置,如下例所示:spring-doc.cadn.net.cn

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 WebFluxspring-doc.cadn.net.cn

UriComponentsBuilder 在两个级别上提供了编码选项:spring-doc.cadn.net.cn

两种选项都会将非ASCII字符和非法字符替换为转义的八进制数。然而,第一个选项还会替换URI变量中具有保留意义的字符。spring-doc.cadn.net.cn

考虑 ";",它在路径中是合法的,但具有保留含义。第一个选项会在 URI 变量中将 ";" 替换为 "%3B",但在 URI 模板中不会替换。相比之下,第二个选项从不替换 ";",因为它在路径中是合法的字符。

在大多数情况下,第一个选项可能会给出预期的结果,因为它将URI变量视为需要完全编码的不透明数据,而第二个选项在URI变量中故意包含保留字符时很有用。当根本不需要展开URI变量时,第二个选项也很有用,因为这样也会对看起来像URI变量的内容进行编码。spring-doc.cadn.net.cn

以下示例使用了第一种选项:spring-doc.cadn.net.cn

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(这隐含了编码)来缩短前面的例子,如下例所示:spring-doc.cadn.net.cn

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模板来缩短它,如下例所示:spring-doc.cadn.net.cn

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 模板。都可以通过自定义策略进行配置,如下例所示:spring-doc.cadn.net.cn

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()

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:spring-doc.cadn.net.cn

  • TEMPLATE_AND_VALUES: 使用 UriComponentsBuilder#encode(),对应于前面列表中的第一个选项,用于预编码URI模板,并在扩展时严格编码URI变量。spring-doc.cadn.net.cn

  • VALUES_ONLY: 不对URI模板进行编码,而是通过UriUtils#encodeUriVariables在将URI变量展开到模板之前应用严格的编码。spring-doc.cadn.net.cn

  • URI_COMPONENT: 使用 UriComponents#encode(),对应于前面列表中的第二个选项,用于在 URI 变量扩展后对 URI 组件值进行编码。spring-doc.cadn.net.cn

  • NONE: 未应用任何编码。spring-doc.cadn.net.cn

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.spring-doc.cadn.net.cn