URI链接

本节描述了Spring Framework中用于准备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创建一个UriBuilder。而UriBuilderFactoryUriBuilder一起提供了一个可插拔的机制,根据共享配置(如基本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在两个级别上公开编码选项:

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

// 结果为 "/hotel%20list/New%20York?q=foo%2Bbar"
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
		.queryParam("q", "{q}")
		.encode()
		.buildAndExpand("New York", "foo+bar")
		.toUri()

// 结果为 "/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);

// 自定义RestTemplate..
RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(factory);

// 自定义WebClient..
WebClient client = WebClient.builder().uriBuilderFactory(factory).build();
val baseUrl = "https://example.com"
val factory = DefaultUriBuilderFactory(baseUrl).apply {
	encodingMode = EncodingMode.TEMPLATE_AND_VALUES
}

// 自定义RestTemplate..
val restTemplate = RestTemplate().apply {
	uriTemplateHandler = factory
}

// 自定义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