Spring Web Reactive | 1. Spring WebFlux | 1.6. URI Links
This section describes the various options available in the Spring Framework for preparing URIs.
1.6.1. UriComponents
Spring MVC and Spring WebFlux
UriComponentsBuilder can create a URI from a URI template with variables, as shown in the following example.
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)
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) A static factory method using a URI template.
- (2) Add or replace URI components.
- (3) Request encoding of the URI template and URI variables.
- (4) Build
UriComponents. - (5) Expand the variables and obtain the
URI.
The example above can be combined into one chain and shortened with buildAndExpand, as shown in the following example.
Java
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("Westin", "123")
.toUri();
Kotlin
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.encode()
.buildAndExpand("Westin", "123")
.toUri()
The following example can be shortened further by going directly to the URI, which implies encoding.
Java
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123");
Kotlin
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123")
You can shorten it even more by using a complete URI template, as shown in the following example.
Java
URI uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123");
Kotlin
val uri = UriComponentsBuilder
.fromUriString("https://example.com/hotels/{hotel}?q={q}")
.build("Westin", "123")
1.6.2. UriBuilder
Spring MVC and Spring WebFlux
UriComponentsBuilder implements UriBuilder. You can create a UriBuilder by using UriBuilderFactory. UriBuilderFactory and UriBuilder provide a pluggable mechanism for building URIs from URI templates according to shared configuration, such as base URL, encoding settings, and other details.
You can configure RestTemplate and WebClient with UriBuilderFactory to customize URI preparation. DefaultUriBuilderFactory uses UriComponentsBuilder internally and is the default implementation of UriBuilderFactory that exposes shared configuration options.
The following example shows how to configure RestTemplate.
Java
// 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);
Kotlin
// 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
The following example configures WebClient.
Java
// 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();
Kotlin
// 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()
You can also use DefaultUriBuilderFactory directly. It is similar to using UriComponentsBuilder, but as shown in the following example, it is an actual instance that holds configuration settings instead of static factory methods.
Java
String baseUrl = "https://example.com";
DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory(baseUrl);
URI uri = uriBuilderFactory.uriString("/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123");
Kotlin
val baseUrl = "https://example.com"
val uriBuilderFactory = DefaultUriBuilderFactory(baseUrl)
val uri = uriBuilderFactory.uriString("/hotels/{hotel}")
.queryParam("q", "{q}")
.build("Westin", "123")
1.6.3. URI Encoding
Spring MVC and Spring WebFlux
UriComponentsBuilder provides encoding options at two levels.
UriComponentsBuilder#encode(): First pre-encodes the URI template, then strictly encodes URI variables when they are expanded.UriComponents#encode(): Encodes URI components after URI variables have been applied.
Both options change non-ASCII characters and invalid characters into escaped octets. However, the first option also replaces characters with reserved meaning that appear in URI variables.
Consider “;”. This character is valid in a path, but its meaning is reserved. The first option replaces “;” so a URI variable contains
%3B, but it does not replace “;” in the URI template. By contrast, the second option does not replace “;” because it is a valid character in the path.
In most cases, the first option gives the desired result because it treats URI variables as opaque data that is fully encoded. The second option is useful when URI variables intentionally contain reserved characters. The second option is also helpful when URI variables are not expanded at all, because it also encodes strings that accidentally look like URI variables.
The following example uses the first option.
Java
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"
Kotlin
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"
As in the following example, you can reduce the example above by going directly to the URI, which implies encoding.
Java
URI uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.build("New York", "foo+bar");
Kotlin
val uri = UriComponentsBuilder.fromPath("/hotel list/{city}")
.queryParam("q", "{q}")
.build("New York", "foo+bar")
You can shorten it even more by using a complete URI template, as shown in the following example.
Java
URI uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
.build("New York", "foo+bar");
Kotlin
val uri = UriComponentsBuilder.fromUriString("/hotel list/{city}?q={q}")
.build("New York", "foo+bar")
WebClient and RestTemplate internally expand and encode URI templates through the UriBuilderFactory strategy. Both can be configured with a custom strategy, as in the following example.
Java
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();
Kotlin
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 encoding approach based on one of the following encoding modes.
TEMPLATE_AND_VALUES: UsesUriComponentsBuilder#encode(), corresponding to the first option in the previous list, to pre-encode the URI template and strictly encode URI variables when they are expanded.VALUES_ONLY: Does not encode the URI template. Instead, it usesUriUtils#encodeUriVariablesto apply exact encoding to URI variables before expanding them into the template.URI_COMPONENT: UsesUriComponents#encode(), corresponding to the second option in the previous list, to encode URI component values after URI variables are expanded.NONE: No encoding is applied.
RestTemplate is set to EncodingMode.URI_COMPONENT for backward compatibility. WebClient depends on the DefaultUriBuilderFactory default value. This changed from EncodingMode.URI_COMPONENT in 5.0.x to EncodingMode.TEMPLATE_AND_VALUES in 5.1.